icoa-cli 1.6.2 → 1.7.0

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,72 +1,141 @@
1
1
  import chalk from 'chalk';
2
2
  import { execSync } from 'node:child_process';
3
3
  import { platform } from 'node:os';
4
+ // ══════════════════════════════════════════════════════════
5
+ // 27 Python Libraries — ALL LOCKED VERSIONS
6
+ // Tested on Python 3.12.x (recommended)
7
+ // ══════════════════════════════════════════════════════════
4
8
  const PYTHON_LIBS = [
5
- { name: 'pwntools', check: 'python3 -c "import pwn"', install: 'pwntools==4.12.0', category: 'Python Libraries' },
6
- { name: 'pycryptodome', check: 'python3 -c "import Crypto"', install: 'pycryptodome==3.20.0', category: 'Python Libraries' },
7
- { name: 'requests', check: 'python3 -c "import requests"', install: 'requests==2.31.0', category: 'Python Libraries' },
8
- { name: 'beautifulsoup4', check: 'python3 -c "import bs4"', install: 'beautifulsoup4==4.12.3', category: 'Python Libraries' },
9
- { name: 'z3-solver', check: 'python3 -c "import z3"', install: 'z3-solver==4.12.6', category: 'Python Libraries' },
10
- { name: 'sympy', check: 'python3 -c "import sympy"', install: 'sympy==1.12', category: 'Python Libraries' },
11
- { name: 'scapy', check: 'python3 -c "import scapy"', install: 'scapy==2.5.0', category: 'Python Libraries' },
12
- { name: 'pillow', check: 'python3 -c "import PIL"', install: 'pillow==10.2.0', category: 'Python Libraries' },
13
- { name: 'numpy', check: 'python3 -c "import numpy"', install: 'numpy==1.26.4', category: 'Python Libraries' },
14
- { name: 'pefile', check: 'python3 -c "import pefile"', install: 'pefile==2023.2.7', category: 'Python Libraries' },
15
- { name: 'capstone', check: 'python3 -c "import capstone"', install: 'capstone==5.0.1', category: 'Python Libraries' },
16
- { name: 'ropper', check: 'python3 -c "import ropper"', install: 'ropper==1.13.8', category: 'Python Libraries' },
17
- { name: 'ROPgadget', check: 'which ROPgadget', install: 'ROPgadget==7.4', category: 'Python Libraries' },
18
- { name: 'flask', check: 'python3 -c "import flask"', install: 'flask==3.0.0', category: 'Python Libraries' },
19
- { name: 'cryptography', check: 'python3 -c "import cryptography"', install: 'cryptography==42.0.0', category: 'Python Libraries' },
20
- { name: 'paramiko', check: 'python3 -c "import paramiko"', install: 'paramiko==3.4.0', category: 'Python Libraries' },
21
- { name: 'python-magic', check: 'python3 -c "import magic"', install: 'python-magic==0.4.27', category: 'Python Libraries' },
22
- { name: 'ipython', check: 'which ipython3 || which ipython', install: 'ipython', category: 'Python Libraries' },
23
- { name: 'angr', check: 'python3 -c "import angr"', install: 'angr', category: 'Python Libraries' },
24
- { name: 'sqlmap', check: 'which sqlmap', install: 'sqlmap', category: 'Python Libraries' },
9
+ // Core CTF
10
+ { name: 'pwntools', check: 'python3 -c "import pwn"', install: 'pwntools==4.12.0', category: 'CTF Core' },
11
+ { name: 'pycryptodome', check: 'python3 -c "import Crypto"', install: 'pycryptodome==3.20.0', category: 'CTF Core' },
12
+ { name: 'z3-solver', check: 'python3 -c "import z3"', install: 'z3-solver==4.12.6', category: 'CTF Core' },
13
+ { name: 'angr', check: 'python3 -c "import angr"', install: 'angr', category: 'CTF Core' },
14
+ // Networking & Web
15
+ { name: 'requests', check: 'python3 -c "import requests"', install: 'requests==2.31.0', category: 'Web & Network' },
16
+ { name: 'beautifulsoup4', check: 'python3 -c "import bs4"', install: 'beautifulsoup4==4.12.3', category: 'Web & Network' },
17
+ { name: 'flask', check: 'python3 -c "import flask"', install: 'flask==3.0.0', category: 'Web & Network' },
18
+ { name: 'scapy', check: 'python3 -c "import scapy"', install: 'scapy==2.5.0', category: 'Web & Network' },
19
+ { name: 'paramiko', check: 'python3 -c "import paramiko"', install: 'paramiko==3.4.0', category: 'Web & Network' },
20
+ // Crypto & Math
21
+ { name: 'sympy', check: 'python3 -c "import sympy"', install: 'sympy==1.12', category: 'Crypto & Math' },
22
+ { name: 'gmpy2', check: 'python3 -c "import gmpy2"', install: 'gmpy2==2.1.5', category: 'Crypto & Math' },
23
+ { name: 'cryptography', check: 'python3 -c "import cryptography"', install: 'cryptography==42.0.0', category: 'Crypto & Math' },
24
+ // Binary & RE
25
+ { name: 'capstone', check: 'python3 -c "import capstone"', install: 'capstone==5.0.1', category: 'Binary & RE' },
26
+ { name: 'ropper', check: 'python3 -c "import ropper"', install: 'ropper==1.13.8', category: 'Binary & RE' },
27
+ { name: 'ROPgadget', check: 'which ROPgadget', install: 'ROPgadget==7.4', category: 'Binary & RE' },
28
+ { name: 'pefile', check: 'python3 -c "import pefile"', install: 'pefile==2023.2.7', category: 'Binary & RE' },
29
+ // Data & Forensics
30
+ { name: 'pillow', check: 'python3 -c "import PIL"', install: 'pillow==10.2.0', category: 'Data & Forensics' },
31
+ { name: 'numpy', check: 'python3 -c "import numpy"', install: 'numpy==1.26.4', category: 'Data & Forensics' },
32
+ { name: 'python-magic', check: 'python3 -c "import magic"', install: 'python-magic==0.4.27', category: 'Data & Forensics' },
33
+ { name: 'yara-python', check: 'python3 -c "import yara"', install: 'yara-python==4.5.0', category: 'Data & Forensics' },
34
+ // Tools
35
+ { name: 'sqlmap', check: 'which sqlmap', install: 'sqlmap', category: 'Security Tools' },
36
+ { name: 'ipython', check: 'which ipython3 || which ipython', install: 'ipython', category: 'Security Tools' },
37
+ { name: 'uncompyle6', check: 'python3 -c "import uncompyle6"', install: 'uncompyle6==3.9.1', category: 'Security Tools' },
38
+ { name: 'rsactftool', check: 'which rsactftool || python3 -c "import rsactftool"', install: 'rsactftool', category: 'Security Tools' },
39
+ { name: 'pngcheck', check: 'python3 -c "import pngcheck"', install: 'pngcheck', category: 'Security Tools' },
40
+ { name: 'volatility3', check: 'python3 -c "import volatility3"', install: 'volatility3', category: 'Security Tools' },
41
+ { name: 'pyserial', check: 'python3 -c "import serial"', install: 'pyserial==3.5', category: 'Security Tools' },
25
42
  ];
43
+ // ══════════════════════════════════════════════════════════
44
+ // 82 System Tools — with brew/apt install commands
45
+ // ══════════════════════════════════════════════════════════
26
46
  const SYSTEM_TOOLS = [
27
- // Editors
28
- { name: 'vim', check: 'which vim', category: 'Editors' },
29
- { name: 'nano', check: 'which nano', category: 'Editors' },
30
- { name: 'tmux', check: 'which tmux', category: 'Editors' },
31
- // Compilers
32
- { name: 'gcc', check: 'which gcc', category: 'Compilers' },
33
- { name: 'g++', check: 'which g++', category: 'Compilers' },
34
- { name: 'make', check: 'which make', category: 'Compilers' },
35
- { name: 'nasm', check: 'which nasm', category: 'Compilers' },
36
- // Python
37
- { name: 'python3', check: 'python3 --version', category: 'Python' },
38
- { name: 'pip3', check: 'pip3 --version', category: 'Python' },
39
- // Network
40
- { name: 'curl', check: 'which curl', category: 'Networking' },
41
- { name: 'wget', check: 'which wget', category: 'Networking' },
42
- { name: 'nc', check: 'which nc', category: 'Networking' },
43
- { name: 'nmap', check: 'which nmap', category: 'Networking' },
44
- { name: 'ssh', check: 'which ssh', category: 'Networking' },
45
- { name: 'socat', check: 'which socat', category: 'Networking' },
46
- // Debuggers
47
- { name: 'gdb', check: 'which gdb', category: 'Debuggers' },
48
- { name: 'ltrace', check: 'which ltrace', category: 'Debuggers' },
49
- { name: 'strace', check: 'which strace', category: 'Debuggers' },
50
- // RE
51
- { name: 'radare2', check: 'which r2', category: 'Reverse Engineering' },
52
- { name: 'objdump', check: 'which objdump', category: 'Reverse Engineering' },
53
- // Forensics
54
- { name: 'binwalk', check: 'which binwalk', category: 'Forensics' },
55
- { name: 'exiftool', check: 'which exiftool', category: 'Forensics' },
56
- { name: 'steghide', check: 'which steghide', category: 'Forensics' },
57
- { name: 'strings', check: 'which strings', category: 'Forensics' },
47
+ // Editors & Terminal (5)
48
+ { name: 'vim', check: 'which vim', brew: 'vim', apt: 'vim', category: 'Editors & Terminal' },
49
+ { name: 'nano', check: 'which nano', brew: 'nano', apt: 'nano', category: 'Editors & Terminal' },
50
+ { name: 'tmux', check: 'which tmux', brew: 'tmux', apt: 'tmux', category: 'Editors & Terminal' },
51
+ { name: 'screen', check: 'which screen', brew: 'screen', apt: 'screen', category: 'Editors & Terminal' },
52
+ { name: 'less', check: 'which less', category: 'Editors & Terminal' },
53
+ // Compilers & Build (8)
54
+ { name: 'gcc', check: 'which gcc', brew: 'gcc', apt: 'gcc', category: 'Compilers & Build' },
55
+ { name: 'g++', check: 'which g++', brew: 'gcc', apt: 'g++', category: 'Compilers & Build' },
56
+ { name: 'make', check: 'which make', brew: 'make', apt: 'make', category: 'Compilers & Build' },
57
+ { name: 'nasm', check: 'which nasm', brew: 'nasm', apt: 'nasm', category: 'Compilers & Build' },
58
+ { name: 'cmake', check: 'which cmake', brew: 'cmake', apt: 'cmake', category: 'Compilers & Build' },
59
+ { name: 'as', check: 'which as', category: 'Compilers & Build' },
60
+ { name: 'ld', check: 'which ld', category: 'Compilers & Build' },
61
+ { name: 'pkg-config', check: 'which pkg-config', brew: 'pkg-config', apt: 'pkg-config', category: 'Compilers & Build' },
62
+ // Python (3)
63
+ { name: 'python3', check: 'python3 --version', brew: 'python@3.12', apt: 'python3', category: 'Python Runtime' },
64
+ { name: 'pip3', check: 'pip3 --version', category: 'Python Runtime' },
65
+ { name: 'python3-venv', check: 'python3 -c "import venv"', apt: 'python3-venv', category: 'Python Runtime' },
66
+ // Networking (12)
67
+ { name: 'curl', check: 'which curl', brew: 'curl', apt: 'curl', category: 'Networking' },
68
+ { name: 'wget', check: 'which wget', brew: 'wget', apt: 'wget', category: 'Networking' },
69
+ { name: 'nc', check: 'which nc', brew: 'netcat', apt: 'netcat-openbsd', category: 'Networking' },
70
+ { name: 'socat', check: 'which socat', brew: 'socat', apt: 'socat', category: 'Networking' },
71
+ { name: 'nmap', check: 'which nmap', brew: 'nmap', apt: 'nmap', category: 'Networking' },
72
+ { name: 'ssh', check: 'which ssh', apt: 'openssh-client', category: 'Networking' },
73
+ { name: 'dig', check: 'which dig', brew: 'bind', apt: 'dnsutils', category: 'Networking' },
74
+ { name: 'whois', check: 'which whois', brew: 'whois', apt: 'whois', category: 'Networking' },
75
+ { name: 'ping', check: 'which ping', apt: 'iputils-ping', category: 'Networking' },
76
+ { name: 'traceroute', check: 'which traceroute', brew: 'traceroute', apt: 'traceroute', category: 'Networking' },
77
+ { name: 'tcpdump', check: 'which tcpdump', brew: 'tcpdump', apt: 'tcpdump', category: 'Networking' },
78
+ { name: 'tshark', check: 'which tshark', brew: 'wireshark', apt: 'tshark', category: 'Networking' },
79
+ // Debuggers & Tracing (5)
80
+ { name: 'gdb', check: 'which gdb', brew: 'gdb', apt: 'gdb', category: 'Debuggers' },
81
+ { name: 'ltrace', check: 'which ltrace', apt: 'ltrace', category: 'Debuggers' },
82
+ { name: 'strace', check: 'which strace', apt: 'strace', category: 'Debuggers' },
83
+ { name: 'objdump', check: 'which objdump', category: 'Debuggers' },
84
+ { name: 'readelf', check: 'which readelf', category: 'Debuggers' },
85
+ // Reverse Engineering (4)
86
+ { name: 'radare2', check: 'which r2', brew: 'radare2', apt: 'radare2', category: 'Reverse Engineering' },
87
+ { name: 'rabin2', check: 'which rabin2', category: 'Reverse Engineering' },
88
+ { name: 'upx', check: 'which upx', brew: 'upx', apt: 'upx', category: 'Reverse Engineering' },
89
+ { name: 'strings', check: 'which strings', category: 'Reverse Engineering' },
90
+ // Forensics (7)
91
+ { name: 'binwalk', check: 'which binwalk', brew: 'binwalk', apt: 'binwalk', category: 'Forensics' },
92
+ { name: 'foremost', check: 'which foremost', apt: 'foremost', category: 'Forensics' },
93
+ { name: 'exiftool', check: 'which exiftool', brew: 'exiftool', apt: 'exiftool', category: 'Forensics' },
94
+ { name: 'steghide', check: 'which steghide', apt: 'steghide', category: 'Forensics' },
58
95
  { name: 'file', check: 'which file', category: 'Forensics' },
59
- { name: 'xxd', check: 'which xxd', category: 'Forensics' },
60
- // Crypto
61
- { name: 'john', check: 'which john', category: 'Crypto' },
62
- { name: 'hashcat', check: 'which hashcat', category: 'Crypto' },
63
- { name: 'openssl', check: 'which openssl', category: 'Crypto' },
64
- // Data
65
- { name: 'jq', check: 'which jq', category: 'Data Processing' },
66
- { name: 'sqlite3', check: 'which sqlite3', category: 'Data Processing' },
67
- // Core
68
- { name: 'git', check: 'which git', category: 'Version Control' },
69
- { name: 'docker', check: 'which docker', category: 'Container' },
96
+ { name: 'xxd', check: 'which xxd', brew: 'vim', apt: 'xxd', category: 'Forensics' },
97
+ { name: 'pdftotext', check: 'which pdftotext', brew: 'poppler', apt: 'poppler-utils', category: 'Forensics' },
98
+ // Crypto & Password (4)
99
+ { name: 'john', check: 'which john', brew: 'john', apt: 'john', category: 'Crypto & Password' },
100
+ { name: 'hashcat', check: 'which hashcat', brew: 'hashcat', apt: 'hashcat', category: 'Crypto & Password' },
101
+ { name: 'openssl', check: 'which openssl', category: 'Crypto & Password' },
102
+ { name: 'gpg', check: 'which gpg', brew: 'gnupg', apt: 'gpg', category: 'Crypto & Password' },
103
+ // Data Processing (8)
104
+ { name: 'jq', check: 'which jq', brew: 'jq', apt: 'jq', category: 'Data Processing' },
105
+ { name: 'sqlite3', check: 'which sqlite3', brew: 'sqlite', apt: 'sqlite3', category: 'Data Processing' },
106
+ { name: 'base64', check: 'which base64', category: 'Data Processing' },
107
+ { name: 'hexdump', check: 'which hexdump', category: 'Data Processing' },
108
+ { name: 'od', check: 'which od', category: 'Data Processing' },
109
+ { name: 'sort', check: 'which sort', category: 'Data Processing' },
110
+ { name: 'uniq', check: 'which uniq', category: 'Data Processing' },
111
+ { name: 'wc', check: 'which wc', category: 'Data Processing' },
112
+ // Archive (6)
113
+ { name: 'unzip', check: 'which unzip', brew: 'unzip', apt: 'unzip', category: 'Archive' },
114
+ { name: 'zip', check: 'which zip', brew: 'zip', apt: 'zip', category: 'Archive' },
115
+ { name: 'tar', check: 'which tar', category: 'Archive' },
116
+ { name: 'gzip', check: 'which gzip', category: 'Archive' },
117
+ { name: 'bzip2', check: 'which bzip2', category: 'Archive' },
118
+ { name: 'xz', check: 'which xz', brew: 'xz', apt: 'xz-utils', category: 'Archive' },
119
+ // Core Unix (16)
120
+ { name: 'cat', check: 'which cat', category: 'Core Unix' },
121
+ { name: 'grep', check: 'which grep', category: 'Core Unix' },
122
+ { name: 'sed', check: 'which sed', category: 'Core Unix' },
123
+ { name: 'awk', check: 'which awk', category: 'Core Unix' },
124
+ { name: 'find', check: 'which find', category: 'Core Unix' },
125
+ { name: 'head', check: 'which head', category: 'Core Unix' },
126
+ { name: 'tail', check: 'which tail', category: 'Core Unix' },
127
+ { name: 'diff', check: 'which diff', category: 'Core Unix' },
128
+ { name: 'patch', check: 'which patch', category: 'Core Unix' },
129
+ { name: 'chmod', check: 'which chmod', category: 'Core Unix' },
130
+ { name: 'chown', check: 'which chown', category: 'Core Unix' },
131
+ { name: 'ln', check: 'which ln', category: 'Core Unix' },
132
+ { name: 'cp', check: 'which cp', category: 'Core Unix' },
133
+ { name: 'mv', check: 'which mv', category: 'Core Unix' },
134
+ { name: 'mkdir', check: 'which mkdir', category: 'Core Unix' },
135
+ { name: 'rm', check: 'which rm', category: 'Core Unix' },
136
+ // Version Control & Container (2)
137
+ { name: 'git', check: 'which git', brew: 'git', apt: 'git', category: 'Git & Docker' },
138
+ { name: 'docker', check: 'which docker', brew: '--cask docker', category: 'Git & Docker' },
70
139
  ];
71
140
  function isInstalled(check) {
72
141
  try {
@@ -79,54 +148,67 @@ function isInstalled(check) {
79
148
  }
80
149
  function getVersion(name) {
81
150
  try {
82
- if (name === 'python3') {
151
+ if (name === 'python3')
83
152
  return execSync('python3 --version', { encoding: 'utf-8' }).trim().replace('Python ', '');
84
- }
85
153
  if (name === 'gcc') {
86
- const out = execSync('gcc --version', { encoding: 'utf-8' });
87
- const match = out.match(/(\d+\.\d+\.\d+)/);
88
- return match ? match[1] : '';
154
+ const m = execSync('gcc --version', { encoding: 'utf-8' }).match(/(\d+\.\d+[\.\d]*)/);
155
+ return m?.[1] || '';
89
156
  }
90
157
  if (name === 'docker') {
91
- const out = execSync('docker --version', { encoding: 'utf-8' });
92
- const match = out.match(/(\d+\.\d+\.\d+)/);
93
- return match ? match[1] : '';
158
+ const m = execSync('docker --version', { encoding: 'utf-8' }).match(/(\d+\.\d+\.\d+)/);
159
+ return m?.[1] || '';
160
+ }
161
+ if (name === 'pip3') {
162
+ const m = execSync('pip3 --version', { encoding: 'utf-8' }).match(/(\d+\.\d+[\.\d]*)/);
163
+ return m?.[1] || '';
164
+ }
165
+ if (name === 'nmap') {
166
+ const m = execSync('nmap --version', { encoding: 'utf-8' }).match(/(\d+\.\d+)/);
167
+ return m?.[1] || '';
94
168
  }
95
169
  }
96
170
  catch { /* ignore */ }
97
171
  return '';
98
172
  }
173
+ function getPythonMajorMinor() {
174
+ try {
175
+ return execSync('python3 -c "import sys; print(f\'{sys.version_info.major}.{sys.version_info.minor}\')"', { encoding: 'utf-8' }).trim();
176
+ }
177
+ catch {
178
+ return '';
179
+ }
180
+ }
99
181
  export function registerEnvCommand(program) {
100
182
  const envCmd = program.command('env').description('Manage competition environment');
101
- // icoa env show status
102
- envCmd
103
- .command('status')
104
- .alias('check')
105
- .description('Check installed tools and libraries')
106
- .action(() => {
107
- showStatus();
108
- });
109
- // icoa env setup — install everything
110
- envCmd
111
- .command('setup')
112
- .description('Install all Python libraries (locked versions)')
113
- .action(async () => {
114
- await installAll();
115
- });
116
- // Default action: show status
117
- envCmd.action(() => {
118
- showStatus();
119
- });
183
+ envCmd.command('status').alias('check').description('Check all 109 tools').action(() => showStatus());
184
+ envCmd.command('setup').description('Install all Python libraries + system tools').action(async () => { await installAll(); });
185
+ envCmd.action(() => showStatus());
120
186
  }
121
187
  function showStatus() {
122
188
  console.log();
123
- console.log(chalk.bold.white(' ICOA Competition Environment'));
124
- console.log(chalk.gray(' ─────────────────────────────────'));
189
+ console.log(chalk.bold.white(' ICOA Competition Environment (109 commands)'));
190
+ console.log(chalk.gray(' ─────────────────────────────────────────────'));
191
+ // Python version check
192
+ const pyVer = getPythonMajorMinor();
193
+ if (pyVer) {
194
+ const fullVer = getVersion('python3');
195
+ if (pyVer === '3.12') {
196
+ console.log(chalk.green(` ✓ Python ${fullVer}`) + chalk.gray(' (recommended)'));
197
+ }
198
+ else if (pyVer === '3.11' || pyVer === '3.13') {
199
+ console.log(chalk.yellow(` ~ Python ${fullVer}`) + chalk.gray(' (3.12.x recommended for full compatibility)'));
200
+ }
201
+ else {
202
+ console.log(chalk.red(` ✗ Python ${fullVer}`) + chalk.gray(' (3.12.x required — some tools may not work)'));
203
+ }
204
+ }
205
+ else {
206
+ console.log(chalk.red(' ✗ Python 3 not found'));
207
+ }
125
208
  console.log();
126
209
  let installed = 0;
127
210
  let missing = 0;
128
211
  let currentCategory = '';
129
- // System tools
130
212
  for (const tool of SYSTEM_TOOLS) {
131
213
  if (tool.category !== currentCategory) {
132
214
  currentCategory = tool.category;
@@ -144,7 +226,6 @@ function showStatus() {
144
226
  missing++;
145
227
  }
146
228
  }
147
- // Python libraries
148
229
  currentCategory = '';
149
230
  for (const lib of PYTHON_LIBS) {
150
231
  if (lib.category !== currentCategory) {
@@ -152,57 +233,93 @@ function showStatus() {
152
233
  console.log(chalk.bold.gray(` ${currentCategory}`));
153
234
  }
154
235
  const ok = isInstalled(lib.check);
155
- const verStr = lib.install ? chalk.gray(` (${lib.install})`) : '';
156
236
  if (ok) {
157
- console.log(chalk.green(` ✓ ${lib.name}`) + verStr);
237
+ console.log(chalk.green(` ✓ ${lib.name}`) + chalk.gray(` (${lib.install || 'latest'})`));
158
238
  installed++;
159
239
  }
160
240
  else {
161
- console.log(chalk.red(` ✗ ${lib.name}`) + (lib.install ? chalk.gray(` → ${lib.install}`) : ''));
241
+ console.log(chalk.red(` ✗ ${lib.name}`) + chalk.gray(` → ${lib.install || 'latest'}`));
162
242
  missing++;
163
243
  }
164
244
  }
245
+ const total = installed + missing;
165
246
  console.log();
166
- console.log(chalk.gray(' ─────────────────────────────────'));
167
- console.log(` ${chalk.green(`✓ ${installed} installed`)} ${missing > 0 ? chalk.red(`✗ ${missing} missing`) : chalk.green('All ready!')}`);
247
+ console.log(chalk.gray(' ─────────────────────────────────────────────'));
248
+ console.log(` ${chalk.green(`✓ ${installed}/${total}`)} ${missing > 0 ? chalk.red(`✗ ${missing} missing`) : chalk.green('All 109 ready!')}`);
168
249
  if (missing > 0) {
169
- console.log();
170
- console.log(chalk.gray(' Install missing Python libs: ') + chalk.white('env setup'));
250
+ console.log(chalk.gray(' Install everything: ') + chalk.white('env setup'));
171
251
  }
172
252
  console.log();
173
253
  }
174
254
  async function installAll() {
175
- console.log();
176
- console.log(chalk.bold.white(' Installing Python libraries (locked versions)...'));
177
255
  console.log();
178
256
  const os = platform();
179
257
  const pipFlag = os === 'darwin' || os === 'linux' ? '--break-system-packages' : '';
180
- let installed = 0;
181
- let failed = 0;
258
+ // Python version warning
259
+ const pyVer = getPythonMajorMinor();
260
+ if (pyVer && pyVer !== '3.12') {
261
+ console.log(chalk.yellow(` Note: Python ${pyVer} detected. Python 3.12.x recommended.`));
262
+ console.log();
263
+ }
264
+ // Install system tools via brew (macOS) or apt (Linux)
265
+ if (os === 'darwin' || os === 'linux') {
266
+ const missingSystem = [];
267
+ for (const tool of SYSTEM_TOOLS) {
268
+ if (!isInstalled(tool.check)) {
269
+ const pkg = os === 'darwin' ? tool.brew : tool.apt;
270
+ if (pkg)
271
+ missingSystem.push(tool);
272
+ }
273
+ }
274
+ if (missingSystem.length > 0) {
275
+ const pm = os === 'darwin' ? 'brew' : 'sudo apt-get';
276
+ console.log(chalk.bold.white(` System Tools (${missingSystem.length} missing)`));
277
+ console.log(chalk.gray(' ─────────────────────────────────────────────'));
278
+ for (const tool of missingSystem) {
279
+ const pkg = os === 'darwin' ? tool.brew : tool.apt;
280
+ process.stdout.write(chalk.gray(` ⏳ ${tool.name}...`));
281
+ try {
282
+ const cmd = os === 'darwin' ? `brew install ${pkg}` : `sudo apt-get install -y ${pkg}`;
283
+ execSync(cmd, { stdio: 'ignore' });
284
+ process.stdout.write('\r');
285
+ console.log(chalk.green(` ✓ ${tool.name}`));
286
+ }
287
+ catch {
288
+ process.stdout.write('\r');
289
+ console.log(chalk.red(` ✗ ${tool.name}`) + chalk.gray(` (${pm} install ${os === 'darwin' ? tool.brew : tool.apt})`));
290
+ }
291
+ }
292
+ console.log();
293
+ }
294
+ }
295
+ // Install Python libraries
296
+ let pipInstalled = 0;
297
+ let pipFailed = 0;
298
+ console.log(chalk.bold.white(` Python Libraries (${PYTHON_LIBS.length} packages)`));
299
+ console.log(chalk.gray(' ─────────────────────────────────────────────'));
182
300
  for (const lib of PYTHON_LIBS) {
183
301
  if (!lib.install)
184
302
  continue;
185
- const alreadyInstalled = isInstalled(lib.check);
186
- if (alreadyInstalled) {
187
- console.log(chalk.green(` ✓ ${lib.name}`) + chalk.gray(` (already installed)`));
188
- installed++;
303
+ if (isInstalled(lib.check)) {
304
+ console.log(chalk.green(` ✓ ${lib.name}`) + chalk.gray(' (installed)'));
305
+ pipInstalled++;
189
306
  continue;
190
307
  }
191
- process.stdout.write(chalk.gray(` ⏳ Installing ${lib.name}...`));
308
+ process.stdout.write(chalk.gray(` ⏳ ${lib.name}...`));
192
309
  try {
193
310
  execSync(`pip3 install ${pipFlag} ${lib.install}`, { stdio: 'ignore' });
194
311
  process.stdout.write('\r');
195
312
  console.log(chalk.green(` ✓ ${lib.name}`) + chalk.gray(` (${lib.install})`));
196
- installed++;
313
+ pipInstalled++;
197
314
  }
198
315
  catch {
199
316
  process.stdout.write('\r');
200
- console.log(chalk.red(` ✗ ${lib.name}`) + chalk.gray(` (install failed)`));
201
- failed++;
317
+ console.log(chalk.red(` ✗ ${lib.name}`) + chalk.gray(' (failed)'));
318
+ pipFailed++;
202
319
  }
203
320
  }
204
321
  console.log();
205
- console.log(chalk.gray(' ─────────────────────────────────'));
206
- console.log(` ${chalk.green(`✓ ${installed} installed`)} ${failed > 0 ? chalk.red(`✗ ${failed} failed`) : chalk.green('All ready!')}`);
322
+ console.log(chalk.gray(' ─────────────────────────────────────────────'));
323
+ console.log(` ${chalk.green(`✓ ${pipInstalled} installed`)} ${pipFailed > 0 ? chalk.red(`✗ ${pipFailed} failed`) : chalk.green('All ready!')}`);
207
324
  console.log();
208
325
  }
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ ${LINE}
36
36
  ${chalk.white('Sydney, Australia')} ${chalk.gray('Jun 27 - Jul 2, 2026')}
37
37
  ${chalk.cyan.underline('https://icoa2026.au')}
38
38
 
39
- ${chalk.gray('CLI-Native Competition Terminal v1.6.2')}
39
+ ${chalk.gray('CLI-Native Competition Terminal v1.7.0')}
40
40
 
41
41
  ${LINE}
42
42
  `;
package/dist/repl.js CHANGED
@@ -6,7 +6,7 @@ import { isActivated, activateToken, isFreeCommand, isDeviceMatch, recordExit, r
6
6
  import { resetTerminalTheme } from './lib/theme.js';
7
7
  import { ensureSandbox, runInSandbox, isDockerAvailable } from './lib/sandbox.js';
8
8
  const INTERCEPT = '__REPL_NO_EXIT__';
9
- const VERSION = '1.6.2';
9
+ const VERSION = '1.7.0';
10
10
  export async function startRepl(program, resumeMode) {
11
11
  const config = getConfig();
12
12
  const connected = isConnected();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "1.6.2",
3
+ "version": "1.7.0",
4
4
  "description": "ICOA CLI — The world's first CLI-native CTF competition terminal",
5
5
  "type": "module",
6
6
  "bin": {