icoa-cli 2.19.99 → 2.19.101

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.
Files changed (45) hide show
  1. package/dist/commands/ai4ctf.js +1 -700
  2. package/dist/commands/connect.js +1 -66
  3. package/dist/commands/ctf.js +1 -620
  4. package/dist/commands/ctf4ai-demo.js +1 -525
  5. package/dist/commands/env.js +1 -737
  6. package/dist/commands/exam.js +1 -2353
  7. package/dist/commands/files.js +1 -52
  8. package/dist/commands/hint.js +1 -119
  9. package/dist/commands/lang.js +1 -155
  10. package/dist/commands/log.js +1 -163
  11. package/dist/commands/note.js +1 -32
  12. package/dist/commands/ref.js +1 -63
  13. package/dist/commands/setup.js +1 -103
  14. package/dist/commands/shell.js +1 -55
  15. package/dist/commands/theme.js +1 -50
  16. package/dist/index.js +1 -225
  17. package/dist/lib/access.js +1 -246
  18. package/dist/lib/budget.js +1 -42
  19. package/dist/lib/colors.js +1 -21
  20. package/dist/lib/config.js +1 -60
  21. package/dist/lib/ctfd-client.js +1 -274
  22. package/dist/lib/demo-exam.js +1 -249
  23. package/dist/lib/demo-flags.js +1 -27
  24. package/dist/lib/demo-stats.js +1 -65
  25. package/dist/lib/exam-client.js +1 -57
  26. package/dist/lib/exam-setup.js +1 -23
  27. package/dist/lib/exam-state.js +1 -112
  28. package/dist/lib/gemini.js +1 -235
  29. package/dist/lib/i18n.js +1 -273
  30. package/dist/lib/log-sync.js +1 -110
  31. package/dist/lib/logger.js +1 -59
  32. package/dist/lib/paper-upgrade.js +1 -117
  33. package/dist/lib/platform.js +1 -86
  34. package/dist/lib/sandbox.js +1 -93
  35. package/dist/lib/terminal.js +1 -49
  36. package/dist/lib/theme.js +1 -108
  37. package/dist/lib/translation.js +1 -66
  38. package/dist/lib/ui.js +1 -80
  39. package/dist/lib/update-check.js +1 -102
  40. package/dist/postinstall.js +1 -48
  41. package/dist/repl.js +1 -1259
  42. package/dist/types/index.d.ts +1 -1
  43. package/dist/types/index.js +1 -38
  44. package/package.json +6 -2
  45. package/translations/sw/i18n-snippet.ts +1 -0
@@ -1,66 +1 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
- import { join, dirname } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- import { getIcoaDir } from './config.js';
5
- import { translateText } from './gemini.js';
6
- const __dirname = dirname(fileURLToPath(import.meta.url));
7
- // Bundled translations shipped with the package (translations/<lang>/<id>.json)
8
- function getBundledPath(lang, challengeId) {
9
- return join(__dirname, '..', '..', 'translations', lang, `${challengeId}.json`);
10
- }
11
- // User-local cache (~/.icoa/translations/)
12
- function getCachePath(lang, challengeId) {
13
- const dir = join(getIcoaDir(), 'translations', lang);
14
- mkdirSync(dir, { recursive: true });
15
- return join(dir, `${challengeId}.json`);
16
- }
17
- export async function getTranslation(text, challengeId, targetLang) {
18
- if (targetLang === 'en')
19
- return text;
20
- // 1. Check bundled static translations (fastest, no API call)
21
- const bundledPath = getBundledPath(targetLang, challengeId);
22
- if (existsSync(bundledPath)) {
23
- try {
24
- const bundled = JSON.parse(readFileSync(bundledPath, 'utf-8'));
25
- if (bundled.translation)
26
- return bundled.translation;
27
- }
28
- catch { /* corrupt, fall through */ }
29
- }
30
- // 2. Check user-local cache
31
- const cachePath = getCachePath(targetLang, challengeId);
32
- if (existsSync(cachePath)) {
33
- try {
34
- const cached = JSON.parse(readFileSync(cachePath, 'utf-8'));
35
- if (cached.translation)
36
- return cached.translation;
37
- }
38
- catch { /* corrupt, fall through */ }
39
- }
40
- // 3. Translate via Gemini API (last resort)
41
- const translation = await translateText(text, getLangName(targetLang));
42
- writeFileSync(cachePath, JSON.stringify({ original: text, translation, timestamp: new Date().toISOString() }));
43
- return translation;
44
- }
45
- function getLangName(code) {
46
- const names = {
47
- en: 'English',
48
- zh: 'Chinese (Simplified)',
49
- ja: 'Japanese',
50
- ko: 'Korean',
51
- es: 'Spanish',
52
- ar: 'Arabic',
53
- fr: 'French',
54
- pt: 'Portuguese (Brazilian)',
55
- ru: 'Russian',
56
- hi: 'Hindi',
57
- de: 'German',
58
- id: 'Indonesian',
59
- th: 'Thai',
60
- vi: 'Vietnamese',
61
- tr: 'Turkish',
62
- uk: 'Ukrainian',
63
- ht: 'Haitian Creole',
64
- };
65
- return names[code] || 'English';
66
- }
1
+ import{existsSync as n,mkdirSync as t,readFileSync as i,writeFileSync as r}from"node:fs";import{join as a,dirname as o}from"node:path";import{fileURLToPath as e}from"node:url";import{getIcoaDir as s}from"./config.js";import{translateText as f}from"./gemini.js";const u=o(e(import.meta.url));export async function getTranslation(o,e,c){if("en"===c)return o;const m=function(n,t){return a(u,"..","..","translations",n,`${t}.json`)}(c,e);if(n(m))try{const n=JSON.parse(i(m,"utf-8"));if(n.translation)return n.translation}catch{}const l=function(n,i){const r=a(s(),"translations",n);return t(r,{recursive:!0}),a(r,`${i}.json`)}(c,e);if(n(l))try{const n=JSON.parse(i(l,"utf-8"));if(n.translation)return n.translation}catch{}const p=await f(o,(h=c,{en:"English",zh:"Chinese (Simplified)",ja:"Japanese",ko:"Korean",es:"Spanish",ar:"Arabic",fr:"French",pt:"Portuguese (Brazilian)",ru:"Russian",hi:"Hindi",de:"German",id:"Indonesian",th:"Thai",vi:"Vietnamese",tr:"Turkish",uk:"Ukrainian",ht:"Haitian Creole"}[h]||"English"));var h;return r(l,JSON.stringify({original:o,translation:p,timestamp:(new Date).toISOString()})),p}
package/dist/lib/ui.js CHANGED
@@ -1,80 +1 @@
1
- import chalk from 'chalk';
2
- import Table from 'cli-table3';
3
- import ora from 'ora';
4
- import { Marked } from 'marked';
5
- import { markedTerminal } from 'marked-terminal';
6
- import { c } from './colors.js';
7
- const marked = new Marked(markedTerminal());
8
- // Wrap the message body in explicit Darcula fg (#A9B7C6). Without this,
9
- // chalk.green('✓ ') emits \x1b[39m at the end and the unstyled msg falls
10
- // back to the terminal's profile fg — which is black on macOS Terminal.app
11
- // default, invisible on our forced #2B2B2B background.
12
- export function printSuccess(msg) {
13
- console.log(chalk.green('✓ ') + c.fg(msg));
14
- }
15
- export function printError(msg) {
16
- console.log(chalk.red('✗ ') + c.fg(msg));
17
- }
18
- export function printWarning(msg) {
19
- console.log(chalk.yellow('⚠ ') + c.fg(msg));
20
- }
21
- export function printInfo(msg) {
22
- console.log(chalk.blue('ℹ ') + c.fg(msg));
23
- }
24
- export function printTable(headers, rows) {
25
- const table = new Table({
26
- head: headers.map((h) => chalk.cyan.bold(h)),
27
- style: { head: [], border: [] },
28
- });
29
- for (const row of rows) {
30
- table.push(row);
31
- }
32
- console.log(table.toString());
33
- }
34
- export function printMarkdown(text) {
35
- const rendered = marked.parse(text);
36
- if (typeof rendered === 'string') {
37
- console.log(rendered);
38
- }
39
- }
40
- // In REPL mode, spinners conflict with readline — use simple log instead
41
- let replMode = false;
42
- export function setReplMode(enabled) { replMode = enabled; }
43
- export function createSpinner(text) {
44
- if (replMode) {
45
- // Fake spinner that just logs — no terminal cursor manipulation
46
- const fake = {
47
- text,
48
- start() { console.log(chalk.cyan(' ' + text)); return fake; },
49
- stop() { return fake; },
50
- succeed(msg) { console.log(chalk.green(' ✓ ' + msg)); return fake; },
51
- fail(msg) { console.log(chalk.red(' ✗ ' + msg)); return fake; },
52
- info(msg) { console.log(chalk.blue(' ℹ ' + msg)); return fake; },
53
- warn(msg) { console.log(chalk.yellow(' ⚠ ' + msg)); return fake; },
54
- };
55
- return fake;
56
- }
57
- return ora({ text, color: 'cyan' });
58
- }
59
- export function formatCountdown(targetDate) {
60
- const now = new Date();
61
- const diff = targetDate.getTime() - now.getTime();
62
- if (diff <= 0)
63
- return '00:00:00';
64
- const hours = Math.floor(diff / (1000 * 60 * 60));
65
- const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
66
- const seconds = Math.floor((diff % (1000 * 60)) / 1000);
67
- return [
68
- hours.toString().padStart(2, '0'),
69
- minutes.toString().padStart(2, '0'),
70
- seconds.toString().padStart(2, '0'),
71
- ].join(':');
72
- }
73
- export function printHeader(title) {
74
- console.log();
75
- console.log(chalk.cyan.bold(` ${title}`));
76
- console.log(chalk.cyan(' ' + '─'.repeat(title.length + 4)));
77
- }
78
- export function printKeyValue(key, value) {
79
- console.log(` ${chalk.gray(key + ':')} ${c.fg(value)}`);
80
- }
1
+ import chalk from"chalk";import o from"cli-table3";import ora from"ora";import{Marked as t}from"marked";import{markedTerminal as n}from"marked-terminal";import{c as e}from"./colors.js";const r=new t(n());export function printSuccess(o){console.log(chalk.green("✓ ")+e.fg(o))}export function printError(o){console.log(chalk.red("✗ ")+e.fg(o))}export function printWarning(o){console.log(chalk.yellow("⚠ ")+e.fg(o))}export function printInfo(o){console.log(chalk.blue("ℹ ")+e.fg(o))}export function printTable(t,n){const e=new o({head:t.map(o=>chalk.cyan.bold(o)),style:{head:[],border:[]}});for(const o of n)e.push(o);console.log(e.toString())}export function printMarkdown(o){const t=r.parse(o);"string"==typeof t&&console.log(t)}let l=!1;export function setReplMode(o){l=o}export function createSpinner(o){if(l){const t={text:o,start:()=>(console.log(chalk.cyan(" "+o)),t),stop:()=>t,succeed:o=>(console.log(chalk.green(" ✓ "+o)),t),fail:o=>(console.log(chalk.red(" ✗ "+o)),t),info:o=>(console.log(chalk.blue(" ℹ "+o)),t),warn:o=>(console.log(chalk.yellow(" ⚠ "+o)),t)};return t}return ora({text:o,color:"cyan"})}export function formatCountdown(o){const t=new Date,n=o.getTime()-t.getTime();if(n<=0)return"00:00:00";const e=Math.floor(n/36e5),r=Math.floor(n%36e5/6e4),l=Math.floor(n%6e4/1e3);return[e.toString().padStart(2,"0"),r.toString().padStart(2,"0"),l.toString().padStart(2,"0")].join(":")}export function printHeader(o){console.log(),console.log(chalk.cyan.bold(` ${o}`)),console.log(chalk.cyan(" "+"─".repeat(o.length+4)))}export function printKeyValue(o,t){console.log(` ${chalk.gray(o+":")} ${e.fg(t)}`)}
@@ -1,102 +1 @@
1
- import { readFileSync, writeFileSync, existsSync } from 'node:fs';
2
- import { join, dirname } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- import { platform } from 'node:os';
5
- import chalk from 'chalk';
6
- import { getIcoaDir } from './config.js';
7
- const installCmd = platform() === 'win32'
8
- ? 'npm install -g icoa-cli@latest'
9
- : platform() === 'darwin'
10
- ? 'npm install -g icoa-cli@latest'
11
- : 'sudo npm install -g icoa-cli@latest';
12
- const __dirname = dirname(fileURLToPath(import.meta.url));
13
- const SIX_HOURS_MS = 6 * 60 * 60 * 1000;
14
- function versionsBehind(latest, current) {
15
- // Count PATCH-level diff only (e.g. 2.19.40 vs 2.19.46 = 6)
16
- const l = latest.split('.').map(Number);
17
- const c = current.split('.').map(Number);
18
- if (l[0] !== c[0] || l[1] !== c[1])
19
- return -1; // major/minor diff
20
- return (l[2] ?? 0) - (c[2] ?? 0);
21
- }
22
- function printUpdateBanner(current, latest) {
23
- const behind = versionsBehind(latest, current);
24
- const behindStr = behind > 0 ? chalk.gray(` (${behind} version${behind === 1 ? '' : 's'} behind)`) : '';
25
- const line = chalk.yellow(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
26
- console.log();
27
- console.log(line);
28
- console.log(chalk.bold.yellow(' ⬆ New version available!'));
29
- console.log();
30
- console.log(chalk.white(' Current: ') + chalk.gray('v' + current));
31
- console.log(chalk.white(' Latest: ') + chalk.bold.green('v' + latest) + behindStr);
32
- console.log();
33
- console.log(chalk.white(' Update: ') + chalk.bold.cyan(installCmd));
34
- console.log(line);
35
- console.log();
36
- }
37
- function isNewer(latest, current) {
38
- const l = latest.split('.').map(Number);
39
- const c = current.split('.').map(Number);
40
- for (let i = 0; i < Math.max(l.length, c.length); i++) {
41
- const lv = l[i] ?? 0;
42
- const cv = c[i] ?? 0;
43
- if (lv > cv)
44
- return true;
45
- if (lv < cv)
46
- return false;
47
- }
48
- return false;
49
- }
50
- export function checkForUpdates() {
51
- // Fire-and-forget async check
52
- setTimeout(async () => {
53
- try {
54
- const pkgPath = join(__dirname, '..', '..', 'package.json');
55
- const current = JSON.parse(readFileSync(pkgPath, 'utf-8')).version;
56
- const cacheFile = join(getIcoaDir(), 'update-check.json');
57
- // Check if we already checked recently
58
- if (existsSync(cacheFile)) {
59
- try {
60
- const cache = JSON.parse(readFileSync(cacheFile, 'utf-8'));
61
- if (Date.now() - cache.lastCheck < SIX_HOURS_MS) {
62
- // Still within cooldown — but show banner if cached version is newer
63
- if (cache.latestVersion && isNewer(cache.latestVersion, current)) {
64
- printUpdateBanner(current, cache.latestVersion);
65
- }
66
- return;
67
- }
68
- }
69
- catch {
70
- // Corrupted cache, continue with fresh check
71
- }
72
- }
73
- // Fetch latest version from npm with 5-second timeout
74
- const controller = new AbortController();
75
- const timeout = setTimeout(() => controller.abort(), 5000);
76
- const res = await fetch('https://registry.npmjs.org/icoa-cli/latest', {
77
- signal: controller.signal,
78
- headers: { 'Accept': 'application/json' },
79
- });
80
- clearTimeout(timeout);
81
- if (!res.ok)
82
- return;
83
- const data = await res.json();
84
- const latest = data.version;
85
- if (!latest)
86
- return;
87
- // Save cache
88
- const cache = {
89
- lastCheck: Date.now(),
90
- latestVersion: latest,
91
- };
92
- writeFileSync(cacheFile, JSON.stringify(cache, null, 2));
93
- // Print banner if newer
94
- if (isNewer(latest, current)) {
95
- printUpdateBanner(current, latest);
96
- }
97
- }
98
- catch {
99
- // Completely silent on any failure
100
- }
101
- }, 0);
102
- }
1
+ import{readFileSync as o,writeFileSync as t,existsSync as e}from"node:fs";import{join as n,dirname as s}from"node:path";import{fileURLToPath as r}from"node:url";import{platform as l}from"node:os";import chalk from"chalk";import{getIcoaDir as i}from"./config.js";const a="win32"===l()||"darwin"===l()?"npm install -g icoa-cli@latest":"sudo npm install -g icoa-cli@latest",c=s(r(import.meta.url));function p(o,t){const e=function(o,t){const e=o.split(".").map(Number),n=t.split(".").map(Number);return e[0]!==n[0]||e[1]!==n[1]?-1:(e[2]??0)-(n[2]??0)}(t,o),n=e>0?chalk.gray(` (${e} version${1===e?"":"s"} behind)`):"",s=chalk.yellow(" ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");console.log(),console.log(s),console.log(chalk.bold.yellow(" ⬆ New version available!")),console.log(),console.log(chalk.white(" Current: ")+chalk.gray("v"+o)),console.log(chalk.white(" Latest: ")+chalk.bold.green("v"+t)+n),console.log(),console.log(chalk.white(" Update: ")+chalk.bold.cyan(a)),console.log(s),console.log()}function m(o,t){const e=o.split(".").map(Number),n=t.split(".").map(Number);for(let o=0;o<Math.max(e.length,n.length);o++){const t=e[o]??0,s=n[o]??0;if(t>s)return!0;if(t<s)return!1}return!1}export function checkForUpdates(){setTimeout(async()=>{try{const s=n(c,"..","..","package.json"),r=JSON.parse(o(s,"utf-8")).version,l=n(i(),"update-check.json");if(e(l))try{const t=JSON.parse(o(l,"utf-8"));if(Date.now()-t.lastCheck<216e5)return void(t.latestVersion&&m(t.latestVersion,r)&&p(r,t.latestVersion))}catch{}const a=new AbortController,u=setTimeout(()=>a.abort(),5e3),g=await fetch("https://registry.npmjs.org/icoa-cli/latest",{signal:a.signal,headers:{Accept:"application/json"}});if(clearTimeout(u),!g.ok)return;const f=(await g.json()).version;if(!f)return;const h={lastCheck:Date.now(),latestVersion:f};t(l,JSON.stringify(h,null,2)),m(f,r)&&p(r,f)}catch{}},0)}
@@ -1,49 +1,2 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * Post-install script: shows a progress bar during ICOA CLI setup.
4
- * Runs automatically after `npm install -g icoa-cli`.
5
- */
6
- const steps = [
7
- 'Initializing ICOA CLI...',
8
- 'Loading competition modules...',
9
- 'Configuring exam system...',
10
- 'Setting up references...',
11
- 'Finalizing installation...',
12
- ];
13
- const BAR_WIDTH = 30;
14
- const TOTAL = 100;
15
- function drawBar(percent, label) {
16
- const filled = Math.round((percent / TOTAL) * BAR_WIDTH);
17
- const empty = BAR_WIDTH - filled;
18
- const bar = '\x1b[32m' + '█'.repeat(filled) + '\x1b[90m' + '░'.repeat(empty) + '\x1b[0m';
19
- const pct = String(percent).padStart(3) + '%';
20
- process.stdout.write(`\r ${bar} ${pct} \x1b[90m${label}\x1b[0m`);
21
- }
22
- async function run() {
23
- console.log();
24
- console.log(' \x1b[1m\x1b[37m██╗ ██████╗ ██████╗ █████╗\x1b[0m');
25
- console.log(' \x1b[1m\x1b[37m██║██╔════╝██╔═══██╗██╔══██╗\x1b[0m');
26
- console.log(' \x1b[1m\x1b[37m██║██║ ██║ ██║███████║\x1b[0m');
27
- console.log(' \x1b[1m\x1b[37m██║██║ ██║ ██║██╔══██║\x1b[0m');
28
- console.log(' \x1b[1m\x1b[37m██║╚██████╗╚██████╔╝██║ ██║\x1b[0m');
29
- console.log(' \x1b[1m\x1b[37m╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝\x1b[0m');
30
- console.log();
31
- for (let i = 0; i < steps.length; i++) {
32
- const startPct = Math.round((i / steps.length) * TOTAL);
33
- const endPct = Math.round(((i + 1) / steps.length) * TOTAL);
34
- for (let p = startPct; p <= endPct; p++) {
35
- drawBar(p, steps[i]);
36
- await new Promise((r) => setTimeout(r, 15));
37
- }
38
- }
39
- console.log();
40
- console.log();
41
- console.log(' \x1b[32m✓\x1b[0m ICOA CLI installed successfully!');
42
- console.log();
43
- console.log(' \x1b[90mGet started:\x1b[0m');
44
- console.log(' \x1b[1m\x1b[37micoa\x1b[0m \x1b[90mLaunch and select your mode\x1b[0m');
45
- console.log(' \x1b[1m\x1b[37micoa --help\x1b[0m \x1b[90mShow all commands\x1b[0m');
46
- console.log();
47
- }
48
- run().catch(() => { });
49
- export {};
2
+ const o=["Initializing ICOA CLI...","Loading competition modules...","Configuring exam system...","Setting up references...","Finalizing installation..."];function l(o,l){const e=Math.round(o/100*30),n=30-e,m=""+"█".repeat(e)+""+"░".repeat(n)+"",t=String(o).padStart(3)+"%";process.stdout.write(`\r ${m} ${t} ${l}`)}(async function(){console.log(),console.log(" ██╗ ██████╗ ██████╗ █████╗"),console.log(" ██║██╔════╝██╔═══██╗██╔══██╗"),console.log(" ██║██║ ██║ ██║███████║"),console.log(" ██║██║ ██║ ██║██╔══██║"),console.log(" ██║╚██████╗╚██████╔╝██║ ██║"),console.log(" ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝"),console.log();for(let e=0;e<o.length;e++){const n=Math.round(e/o.length*100),m=Math.round((e+1)/o.length*100);for(let t=n;t<=m;t++)l(t,o[e]),await new Promise(o=>setTimeout(o,15))}console.log(),console.log(),console.log(" ✓ ICOA CLI installed successfully!"),console.log(),console.log(" Get started:"),console.log(" icoa Launch and select your mode"),console.log(" icoa --help Show all commands"),console.log()})().catch(()=>{});export{};