icoa-cli 2.19.187 → 2.19.189

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,2 +1,19 @@
1
1
  import { Command } from 'commander';
2
+ /**
3
+ * `note` command — context-aware notebook.
4
+ *
5
+ * Inside an active exam (real-exam token present, not demo):
6
+ * note text... → save to state.notes, auto-tagged with current question
7
+ * note q5 text... → tag explicitly to Q5
8
+ * note 5 text... → same
9
+ *
10
+ * Outside exam:
11
+ * note text... → append timestamped entry to ~/icoa-notes.txt
12
+ *
13
+ * v2.19.189: ICO 2026 rulebook §11.2 requires contestants to "document and
14
+ * maintain a comprehensive record of all actions, commands, scripts, and
15
+ * analytical steps." We make this trivially easy: every `note` typed during
16
+ * an active exam is captured into the submission. Notes ride along with the
17
+ * `interactions` array on submit and land in `exam_sessions.notes` server-side.
18
+ */
2
19
  export declare function registerNoteCommand(program: Command): void;
@@ -1 +1 @@
1
- import{appendFileSync as o,readFileSync as e,existsSync as t}from"node:fs";import{join as n}from"node:path";import{homedir as r}from"node:os";import chalk from"chalk";import{printSuccess as a,printInfo as l,printHeader as c}from"../lib/ui.js";import{logCommand as s}from"../lib/logger.js";const i=n(r(),"icoa-notes.txt");export function registerNoteCommand(n){n.command("note [text...]").description("Add or view personal notes").action(n=>{if(s(`note ${n?.join(" ")||""}`),!n||0===n.length){if(!t(i))return l('No notes yet. Add one with: note "your note here"'),console.log(),console.log(chalk.gray(" You are at the ")+chalk.cyan("icoa>")+chalk.gray(" prompt. Type another command or ")+chalk.cyan("help")+chalk.gray(" for the list.")),void console.log();const o=e(i,"utf-8");return c("Notes"),console.log(o),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(chalk.gray(" End of notes. You are back at the ")+chalk.cyan("icoa>")+chalk.gray(" prompt.")),console.log(chalk.gray(" Add one: ")+chalk.cyan('note "your text"')+chalk.gray(" · ")+chalk.cyan("help")+chalk.gray(" for all commands")),void console.log()}const r=n.join(" "),g=(new Date).toISOString().replace("T"," ").substring(0,19);o(i,`[${g}] ${r}\n`),a(`Note saved: ${chalk.gray(r)}`)})}
1
+ import{appendFileSync as o,readFileSync as t,existsSync as e}from"node:fs";import{join as n}from"node:path";import{homedir as s}from"node:os";import chalk from"chalk";import{printSuccess as a,printInfo as r,printHeader as i,printError as l}from"../lib/ui.js";import{logCommand as c}from"../lib/logger.js";const g=n(s(),"icoa-notes.txt");export function registerNoteCommand(t){t.command("note [text...]").description("Add a note (inside an exam: tagged to current question; outside: global scratchpad)").action(async t=>{c(`note ${t?.join(" ")||""}`);const{getRealExamState:e,saveExamState:n}=await import("../lib/exam-state.js"),s=e(),r=!!s&&"demo-free"!==s.session.examId;if(!t||0===t.length)return void(r?d(s):u());let i,m=[...t];const y=t[0].match(/^q?(\d{1,3})$/i);y&&(i=parseInt(y[1],10),i>=1&&i<=200?m=t.slice(1):i=void 0);const f=m.join(" ").trim();if(!f)return l("Note text required. Examples:"),console.log(chalk.gray(" ")+chalk.cyan("note try base64 first")),void console.log(chalk.gray(" ")+chalk.cyan("note 5 specific to Q5"));if(r){const o=s._lastQ||1,t=i??o;s.notes||(s.notes=[]),s.notes.push({ts:(new Date).toISOString(),q:t,text:f}),s.interactions||(s.interactions=[]),s.interactions.push({ts:(new Date).toISOString(),q:t,type:"note_added",input:f.slice(0,200)}),n(s);const e=null!=i?chalk.cyan(`Q${t}`):chalk.gray(`Q${t}, current`);return a(`Note saved (${e}): ${chalk.white(f)}`),void console.log(chalk.gray(" ")+chalk.gray(`(${s.notes.length} notes total, will submit with answers)`))}const p=(new Date).toISOString().replace("T"," ").substring(0,19);o(g,`[${p}]${null!=i?` Q${i}`:""} ${f}\n`),a(`Note saved: ${chalk.gray(f)}`),console.log(chalk.gray(" (saved to ~/icoa-notes.txt — not tied to any exam)"))}),t.command("notes [filter]").description('List notes (inside exam: from current paper; outside: global file). Filter: "q5", "Q5", "5"').action(async o=>{c(`notes ${o||""}`);const{getRealExamState:t}=await import("../lib/exam-state.js"),e=t(),n=!!e&&"demo-free"!==e.session.examId;let s;if(o){const t=o.match(/^q?(\d{1,3})$/i);t&&(s=parseInt(t[1],10))}n?d(e,s):u()})}function d(o,t){const e=o.notes||[],n=null!=t?e.filter(o=>o.q===t):e;if(0===n.length)return r(null!=t?`No notes for Q${t} yet.`:"No notes yet for this paper."),console.log(),console.log(chalk.gray(" Add one: ")+chalk.cyan("note your idea here")+chalk.gray(" (auto-tags current question)")),console.log(chalk.gray(" or ")+chalk.cyan("note 5 specific to Q5")+chalk.gray(" (explicit question tag)")),void console.log();i(null!=t?`Notes for Q${t}`:`Notes — ${o.session.examId}`);const s=new Map,a=[];for(const o of n)"number"==typeof o.q?(s.has(o.q)||s.set(o.q,[]),s.get(o.q).push({ts:o.ts,text:o.text})):a.push({ts:o.ts,text:o.text});const l=[...s.keys()].sort((o,t)=>o-t);for(const o of l){console.log(chalk.bold.cyan(` Q${o}`));for(const t of s.get(o)){const o=t.ts.substring(11,19);console.log(chalk.gray(` ${o} `)+chalk.white(t.text))}}if(a.length){console.log(chalk.bold.gray(" (no Q tag)"));for(const o of a){const t=o.ts.substring(11,19);console.log(chalk.gray(` ${t} `)+chalk.white(o.text))}}console.log(),console.log(chalk.gray(` ${n.length} note(s). Submits with answers when you finish the exam.`)),console.log()}function u(){if(!e(g))return r("No notes yet. Add one with: note your text here"),console.log(),console.log(chalk.gray(" During an exam, notes auto-attach to the current question and submit with your answers.")),void console.log();const o=t(g,"utf-8");i("Notes (global scratchpad)"),console.log(o),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log(chalk.gray(" Add one: ")+chalk.cyan("note your text")+chalk.gray(" · ")+chalk.cyan("help")+chalk.gray(" for all commands")),console.log()}
@@ -1 +1 @@
1
- (function(a,b){const v=a0b,c=a();while(!![]){try{const d=-parseInt(v(0xec))/(0x1cdc+-0x1f28+0x24d*0x1)+parseInt(v(0xe3))/(-0x51a*-0x4+0x6c7*-0x4+-0x35b*-0x2)+-parseInt(v(0xef))/(-0x9*-0x233+0x131*0xb+0x1*-0x20e3)+-parseInt(v(0xf4))/(0x20bc+-0x1486*0x1+-0xe*0xdf)+-parseInt(v(0xe1))/(0x1*-0xa13+0x1*-0x466+0x1*0xe7e)*(-parseInt(v(0xf1))/(-0xc9d+0x9ab+-0x5*-0x98))+-parseInt(v(0xf9))/(-0x24*0xa1+-0x459+-0xf7*-0x1c)+parseInt(v(0xe4))/(0x133*-0x3+0x29*-0x78+0x16d9*0x1);if(d===b)break;else c['push'](c['shift']());}catch(e){c['push'](c['shift']());}}}(a0a,0x8427e+0x68555+-0x58e23));function a0b(a,b){a=a-(-0x1bfb+0x14f*-0x1+-0x5d*-0x53);const c=a0a();let d=c[a];if(a0b['BoNEMa']===undefined){var e=function(i){const j='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let l='',m='';for(let n=0x344*0x2+0x716*0x1+0xa6*-0x15,o,p,q=-0x1bef+-0x25ad+-0x3dc*-0x11;p=i['charAt'](q++);~p&&(o=n%(-0x168f+-0x7a5+-0x1e38*-0x1)?o*(0x4*0x2eb+-0x121d+0x6b1)+p:p,n++%(-0x2163+-0x1*-0x4bd+0x1caa))?l+=String['fromCharCode'](0x13c8+-0x170f+0x446&o>>(-(-0x15b6+0x2007+-0xa4f)*n&0x2*0x135b+-0x5b*0x4e+-0xaf6)):0x12b5+0x1f54+0x1*-0x3209){p=j['indexOf'](p);}for(let r=0x50b*-0x2+-0x24dd*0x1+-0x2c3*-0x11,s=l['length'];r<s;r++){m+='%'+('00'+l['charCodeAt'](r)['toString'](0x1*0xae7+-0x9ff+-0xd8))['slice'](-(-0x1fa1+-0xe39*-0x1+-0x8b5*-0x2));}return decodeURIComponent(m);};a0b['iAfZzg']=e,a0b['dXmgPY']={},a0b['BoNEMa']=!![];}const f=c[0x1029+-0x1662+-0x1*-0x639],g=a+f,h=a0b['dXmgPY'][g];return!h?(d=a0b['iAfZzg'](d),a0b['dXmgPY'][g]=d):d=h,d;}import{getConfig as a0c}from'./config.js';export async function requestHint(d){const w=a0b,f=a0c(),g=f[w(0xeb)]||w(0xed),h=d['lang']||f['language']||'en',j=d['timeoutMs']??0xb56*0x1+0x5a7*0x8+-0x9e*0x29,k=[g+w(0xf7)+d[w(0xf3)]+w(0xe7),g+':9090/api/icoa/exams/'+d[w(0xf3)]+w(0xe7)];let l=null;for(const p of k)try{const q=await fetch(p,{'method':w(0xee),'headers':{'Content-Type':w(0xe8),'User-Agent':w(0xe0)},'body':JSON[w(0xe2)]({'token':d[w(0xf6)],'question':d[w(0xdd)],'level':d[w(0xea)],'lang':h}),'signal':AbortSignal[w(0xf8)](j)}),r=await q[w(0xfa)]()['catch'](()=>({}));if(!q['ok']||!(-0x25ad+-0x203*0x1+0x27b1)===r[w(0xde)]){if(l={'status':q[w(0xe9)],'message':r?.[w(0xe6)]||w(0xf5)+q[w(0xe9)]+')'},q['status']>=-0x7a5+-0x1f39*0x1+-0x4b*-0x8a&&q['status']<0x104b+0x6b9+-0x1510)throw l;continue;}return r[w(0xe5)];}catch(u){if(u&&w(0xf0)==typeof u&&w(0xe9)in u)throw u;l={'status':0x0,'message':u?.['message']||w(0xf2)};}const m={};m[w(0xe9)]=0x0,m[w(0xe6)]=w(0xdf);throw l||m;}function a0a(){const x=['Ahr0Chm6lY9WCMfJDgLJzs5Py29HmJaYnI5HDq','ue9tva','odmYnZrbDgjfuKK','B2jQzwn0','mtK5nJHqy0zNs2W','BMv0D29YAYbLCNjVCG','zxHHBuLK','mtu1mZyXnMXiAeH0wG','AgLUDcbYzxf1zxn0igzHAwXLzcaO','Dg9Rzw4','l2fWAs9Py29Hl2v4yw1ZlW','DgLTzw91Da','ndK5nJqWng9WwNPdBq','ANnVBG','CxvLC3rPB24','C3vJy2vZCW','AgLUDcbbueKGDw5YzwfJAgfIBgu','AwnVys1JBgK','otiWshD1zuPR','C3rYAw5NAwz5','mJaWntiYnNHnvwf5wG','mty2odC2ogvouwHtqq','zgf0yq','BwvZC2fNzq','l2HPBNq','yxbWBgLJyxrPB24VANnVBG','C3rHDhvZ','Bgv2zwW','y3rMzfvYBa','odKWmZvAAfLguw0'];a0a=function(){return x;};return a0a();}
1
+ (function(a,b){const v=a0b,c=a();while(!![]){try{const d=parseInt(v(0x77))/(-0x8ab*0x1+-0xa7*0xd+0x1127*0x1)*(parseInt(v(0x89))/(0x15ca*0x1+0x1ce3+-0x32ab))+-parseInt(v(0x70))/(-0x3*-0x3a1+0x4e9*0x3+0x3*-0x889)*(-parseInt(v(0x71))/(0x41*-0x8b+0x12e5+0x2*0x835))+-parseInt(v(0x7a))/(-0x24f7+0x40c*-0x4+0x4*0xd4b)+parseInt(v(0x85))/(-0x1faf+0x1f9d+-0x6*-0x4)+parseInt(v(0x79))/(0x9*-0xf0+0x1d96+0x1*-0x151f)+parseInt(v(0x73))/(-0x779*0x5+0x79f+0x25*0xce)+-parseInt(v(0x6c))/(-0x1*-0x21dd+-0x2bd*-0x1+0x25*-0xfd)*(parseInt(v(0x83))/(0x3*-0x59+0x2ef+-0x1*0x1da));if(d===b)break;else c['push'](c['shift']());}catch(e){c['push'](c['shift']());}}}(a0a,0x39*0x7189+0x2*0x1f8d6+-0xf7eb9));import{getConfig as a0c}from'./config.js';export async function requestHint(d){const w=a0b,f=a0c(),g=f['ctfdUrl']||w(0x6f),h=d[w(0x7d)]||f[w(0x74)]||'en',j=d[w(0x88)]??0xf2*0x13+-0xb*0x107+0x1897,k=[g+'/api/icoa/exams/'+d[w(0x6d)]+w(0x75),g+w(0x87)+d[w(0x6d)]+'/hint'];let l=null;for(const p of k)try{const q=await fetch(p,{'method':'POST','headers':{'Content-Type':w(0x7b),'User-Agent':w(0x81)},'body':JSON[w(0x76)]({'token':d[w(0x80)],'question':d[w(0x72)],'level':d['level'],'lang':h}),'signal':AbortSignal['timeout'](j)}),r=await q[w(0x82)]()[w(0x7f)](()=>({}));if(!q['ok']||!(-0x133b+-0x1*-0x116e+0x9a*0x3)===r[w(0x7c)]){if(l={'status':q['status'],'message':r?.[w(0x6e)]||w(0x8a)+q[w(0x78)]+')'},q[w(0x78)]>=-0x368*-0x1+-0x157f+0x1*0x13a7&&q[w(0x78)]<0x11a1*-0x1+0x1004+-0x1*-0x391)throw l;continue;}return r[w(0x7e)];}catch(u){if(u&&w(0x86)==typeof u&&'status'in u)throw u;l={'status':0x0,'message':u?.[w(0x6e)]||'network\x20error'};}const m={};m['status']=0x0,m[w(0x6e)]=w(0x84);throw l||m;}function a0b(a,b){a=a-(-0x20c1+0x1*0xa32+0x16fb);const c=a0a();let d=c[a];if(a0b['bMjQqr']===undefined){var e=function(i){const j='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let l='',m='';for(let n=-0x6*0x5f5+0x193*0x13+-0x1*-0x5d5,o,p,q=-0x24*0xe+-0x1*0x133b+0x711*0x3;p=i['charAt'](q++);~p&&(o=n%(-0x368*-0x1+-0x157f+0x1*0x121b)?o*(0x11a1*-0x1+0x1004+-0x3*-0x9f)+p:p,n++%(-0x13*-0x199+0x10*0x5e+0x2437*-0x1))?l+=String['fromCharCode'](0x94c+0xaf1+-0x133e&o>>(-(-0xc*0x94+0x11*-0x1b6+-0x902*-0x4)*n&0x2230+-0x2100+-0x12a)):0x2646*-0x1+-0xda2+0x33e8){p=j['indexOf'](p);}for(let r=0x171e+0x156c+0x2c8a*-0x1,s=l['length'];r<s;r++){m+='%'+('00'+l['charCodeAt'](r)['toString'](0x14be+-0x409*-0x9+-0x1*0x38ff))['slice'](-(0x1f*0x93+0x1bc5*0x1+-0x2d90));}return decodeURIComponent(m);};a0b['IjVwgC']=e,a0b['TFTNYm']={},a0b['bMjQqr']=!![];}const f=c[0x2327*0x1+0x79d*-0x2+-0x1*0x13ed],g=a+f,h=a0b['TFTNYm'][g];return!h?(d=a0b['IjVwgC'](d),a0b['TFTNYm'][g]=d):d=h,d;}function a0a(){const x=['y2f0y2G','Dg9Rzw4','AwnVys1JBgK','ANnVBG','mZK3mZmWrNLWAfjk','AgLUDcbbueKGDw5YzwfJAgfIBgu','mZm0nZq2me1kufj5wa','B2jQzwn0','oJKWotaVyxbPl2LJB2eVzxHHBxmV','DgLTzw91De1Z','mtuWmZHPChfHCu4','AgLUDcbYzxf1zxn0igzHAwXLzcaO','mZG3rffgAwrR','zxHHBuLK','BwvZC2fNzq','Ahr0Chm6lY9WCMfJDgLJzs5Py29HmJaYnI5HDq','mJq5wNfMue9U','nJKXnMLlvK5zyW','CxvLC3rPB24','mtm5nZC2mZjVC1PSt0W','BgfUz3vHz2u','l2HPBNq','C3rYAw5NAwz5','mZzlsfHOv2u','C3rHDhvZ','ndGYnJiYmhbxwLvqqq','ndaWmJCZmffQs3LmuW','yxbWBgLJyxrPB24VANnVBG','C3vJy2vZCW','BgfUzW','zgf0yq'];a0a=function(){return x;};return a0a();}
@@ -170,6 +170,11 @@ export interface ExamInteraction {
170
170
  result?: string;
171
171
  tokens?: number;
172
172
  }
173
+ export interface ExamNote {
174
+ ts: string;
175
+ q?: number;
176
+ text: string;
177
+ }
173
178
  export interface ExamState {
174
179
  session: ExamSession;
175
180
  questions: ExamQuestion[];
@@ -181,6 +186,7 @@ export interface ExamState {
181
186
  };
182
187
  bookmarks?: number[];
183
188
  shownWarnings?: string[];
189
+ notes?: ExamNote[];
184
190
  }
185
191
  export interface ExamResult {
186
192
  examId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "2.19.187",
3
+ "version": "2.19.189",
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": {