icoa-cli 1.9.0 → 2.0.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.
- package/dist/commands/env.js +33 -17
- package/dist/index.js +1 -1
- package/dist/repl.js +38 -13
- package/package.json +1 -1
package/dist/commands/env.js
CHANGED
|
@@ -61,9 +61,9 @@ const SYSTEM_TOOLS = [
|
|
|
61
61
|
{ name: 'as', check: CMD('as'), category: 'Compilers & Build' },
|
|
62
62
|
{ name: 'ld', check: CMD('ld'), category: 'Compilers & Build' },
|
|
63
63
|
{ name: 'pkg-config', check: CMD('pkg-config'), brew: 'pkg-config', apt: 'pkg-config', category: 'Compilers & Build' },
|
|
64
|
-
// Python (3)
|
|
65
|
-
{ name: 'python3', check: W ? 'python --version' : 'python3 --version', brew: 'python@3.12', apt: 'python3', choco: 'python312', category: 'Python Runtime' },
|
|
66
|
-
{ name: 'pip3', check: W ? 'pip --version' : 'pip3 --version', category: 'Python Runtime' },
|
|
64
|
+
// Python (3) — prefer brew 3.12 on macOS
|
|
65
|
+
{ name: 'python3', check: process.platform === 'darwin' ? '/opt/homebrew/opt/python@3.12/bin/python3.12 --version' : (W ? 'python --version' : 'python3 --version'), brew: 'python@3.12', apt: 'python3', choco: 'python312', category: 'Python Runtime' },
|
|
66
|
+
{ name: 'pip3', check: process.platform === 'darwin' ? '/opt/homebrew/opt/python@3.12/bin/pip3.12 --version' : (W ? 'pip --version' : 'pip3 --version'), category: 'Python Runtime' },
|
|
67
67
|
{ name: 'python3-venv', check: W ? 'python -c "import venv"' : 'python3 -c "import venv"', apt: 'python3-venv', category: 'Python Runtime' },
|
|
68
68
|
// Networking (12)
|
|
69
69
|
{ name: 'curl', check: CMD('curl'), brew: 'curl', apt: 'curl', choco: 'curl', category: 'Networking' },
|
|
@@ -160,8 +160,8 @@ function isInstalled(check) {
|
|
|
160
160
|
function getVersion(name) {
|
|
161
161
|
// Version commands for each tool
|
|
162
162
|
const versionCmds = {
|
|
163
|
-
'python3': 'python3 --version',
|
|
164
|
-
'pip3': 'pip3 --version',
|
|
163
|
+
'python3': process.platform === 'darwin' ? '/opt/homebrew/opt/python@3.12/bin/python3.12 --version' : 'python3 --version',
|
|
164
|
+
'pip3': process.platform === 'darwin' ? '/opt/homebrew/opt/python@3.12/bin/pip3.12 --version' : 'pip3 --version',
|
|
165
165
|
'gcc': 'gcc --version',
|
|
166
166
|
'g++': 'g++ --version',
|
|
167
167
|
'make': 'make --version',
|
|
@@ -237,6 +237,13 @@ function getNodeInfo() {
|
|
|
237
237
|
return `Node.js ${process.version}`;
|
|
238
238
|
}
|
|
239
239
|
function getPythonMajorMinor() {
|
|
240
|
+
// On macOS, prefer brew Python 3.12
|
|
241
|
+
if (process.platform === 'darwin') {
|
|
242
|
+
try {
|
|
243
|
+
return execSync('/opt/homebrew/opt/python@3.12/bin/python3.12 -c "import sys; print(f\'{sys.version_info.major}.{sys.version_info.minor}\')"', { encoding: 'utf-8' }).trim();
|
|
244
|
+
}
|
|
245
|
+
catch { /* fall through */ }
|
|
246
|
+
}
|
|
240
247
|
try {
|
|
241
248
|
return execSync('python3 -c "import sys; print(f\'{sys.version_info.major}.{sys.version_info.minor}\')"', { encoding: 'utf-8' }).trim();
|
|
242
249
|
}
|
|
@@ -244,6 +251,20 @@ function getPythonMajorMinor() {
|
|
|
244
251
|
return '';
|
|
245
252
|
}
|
|
246
253
|
}
|
|
254
|
+
function getPythonFullVersion() {
|
|
255
|
+
if (process.platform === 'darwin') {
|
|
256
|
+
try {
|
|
257
|
+
return execSync('/opt/homebrew/opt/python@3.12/bin/python3.12 --version', { encoding: 'utf-8' }).trim().replace('Python ', '');
|
|
258
|
+
}
|
|
259
|
+
catch { /* fall through */ }
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
return execSync('python3 --version', { encoding: 'utf-8' }).trim().replace('Python ', '');
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
return '';
|
|
266
|
+
}
|
|
267
|
+
}
|
|
247
268
|
export function registerEnvCommand(program) {
|
|
248
269
|
const envCmd = program.command('env').description('Manage competition environment');
|
|
249
270
|
envCmd.command('status').alias('check').description('Check all 109 tools').action(() => showStatus());
|
|
@@ -261,19 +282,14 @@ function showStatus() {
|
|
|
261
282
|
console.log(chalk.gray(' Package: ') + chalk.white(pm));
|
|
262
283
|
console.log(chalk.gray(' Target: ') + chalk.white(`Python ${PYTHON_TARGET}.x`));
|
|
263
284
|
console.log(chalk.gray(' ─────────────────────────────────────────────'));
|
|
264
|
-
// Python version check
|
|
285
|
+
// Python version check — use brew 3.12 on macOS
|
|
265
286
|
const pyVer = getPythonMajorMinor();
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
console.log(chalk.yellow(` ~ Python ${fullVer}`) + chalk.gray(' (3.12.x recommended for full compatibility)'));
|
|
273
|
-
}
|
|
274
|
-
else {
|
|
275
|
-
console.log(chalk.red(` ✗ Python ${fullVer}`) + chalk.gray(' (3.12.x required — some tools may not work)'));
|
|
276
|
-
}
|
|
287
|
+
const fullVer = getPythonFullVersion();
|
|
288
|
+
if (pyVer === '3.12') {
|
|
289
|
+
console.log(chalk.green(` ✓ Python ${fullVer}`) + chalk.gray(' (recommended)'));
|
|
290
|
+
}
|
|
291
|
+
else if (pyVer) {
|
|
292
|
+
console.log(chalk.yellow(` ~ Python ${fullVer}`) + chalk.gray(' (3.12.x recommended)'));
|
|
277
293
|
}
|
|
278
294
|
else {
|
|
279
295
|
console.log(chalk.red(' ✗ Python 3 not found'));
|
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
|
|
39
|
+
${chalk.gray('CLI-Native Competition Terminal v2.0.0')}
|
|
40
40
|
|
|
41
41
|
${LINE}
|
|
42
42
|
`;
|
package/dist/repl.js
CHANGED
|
@@ -7,8 +7,27 @@ import { resetTerminalTheme } from './lib/theme.js';
|
|
|
7
7
|
import { ensureSandbox, runInSandbox, isDockerAvailable } from './lib/sandbox.js';
|
|
8
8
|
import { logCommand } from './lib/logger.js';
|
|
9
9
|
import { startLogSync, stopLogSync } from './lib/log-sync.js';
|
|
10
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { homedir } from 'node:os';
|
|
13
|
+
// Competition workspace — all system commands restricted here
|
|
14
|
+
const WORKSPACE = join(homedir(), 'icoa-workspace');
|
|
15
|
+
function ensureWorkspace() {
|
|
16
|
+
if (!existsSync(WORKSPACE))
|
|
17
|
+
mkdirSync(WORKSPACE, { recursive: true });
|
|
18
|
+
return WORKSPACE;
|
|
19
|
+
}
|
|
20
|
+
// Blocked commands — security risk
|
|
21
|
+
const BLOCKED_COMMANDS = new Set([
|
|
22
|
+
'sudo', 'su', 'doas', 'pkexec', // privilege escalation
|
|
23
|
+
'brew', 'apt', 'apt-get', 'yum', 'choco', // package managers (use env setup)
|
|
24
|
+
'npm', 'npx', 'pip', 'pip3', // package install (use env setup)
|
|
25
|
+
'shutdown', 'reboot', 'halt', // system control
|
|
26
|
+
'mkfs', 'fdisk', 'dd', // disk operations
|
|
27
|
+
'iptables', 'ufw', // firewall
|
|
28
|
+
]);
|
|
10
29
|
const INTERCEPT = '__REPL_NO_EXIT__';
|
|
11
|
-
const VERSION = '
|
|
30
|
+
const VERSION = '2.0.0';
|
|
12
31
|
export async function startRepl(program, resumeMode) {
|
|
13
32
|
const config = getConfig();
|
|
14
33
|
const connected = isConnected();
|
|
@@ -83,7 +102,9 @@ export async function startRepl(program, resumeMode) {
|
|
|
83
102
|
console.log();
|
|
84
103
|
}
|
|
85
104
|
else if (activated) {
|
|
105
|
+
ensureWorkspace();
|
|
86
106
|
console.log(chalk.green(' Welcome, competitor! Ready to hack.'));
|
|
107
|
+
console.log(chalk.gray(` Workspace: ${WORKSPACE}`));
|
|
87
108
|
console.log();
|
|
88
109
|
console.log(chalk.gray(' Quick Start'));
|
|
89
110
|
console.log(chalk.gray(' ─────────────'));
|
|
@@ -193,20 +214,24 @@ export async function startRepl(program, resumeMode) {
|
|
|
193
214
|
'log', 'lang', 'setup', 'env', 'model', 'ctf',
|
|
194
215
|
];
|
|
195
216
|
if (!knownCommands.includes(cmd)) {
|
|
196
|
-
//
|
|
217
|
+
// Block dangerous commands
|
|
218
|
+
if (BLOCKED_COMMANDS.has(cmd)) {
|
|
219
|
+
console.log(chalk.red(` Blocked: ${cmd} is not allowed during competition.`));
|
|
220
|
+
console.log();
|
|
221
|
+
rl.prompt();
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
// Force Python 3.12 — rewrite python3/python to 3.12 binaries
|
|
197
225
|
let resolvedInput = input;
|
|
198
226
|
if (process.platform === 'darwin') {
|
|
199
227
|
const py12 = '/opt/homebrew/opt/python@3.12/bin/python3.12';
|
|
200
|
-
const pip12 = '/opt/homebrew/opt/python@3.12/bin/pip3.12';
|
|
201
228
|
resolvedInput = resolvedInput
|
|
202
229
|
.replace(/^python3?\s/, `${py12} `)
|
|
203
|
-
.replace(/^
|
|
204
|
-
if (resolvedInput === 'python3' || resolvedInput === 'python')
|
|
205
|
-
resolvedInput = py12;
|
|
206
|
-
if (resolvedInput === 'pip3' || resolvedInput === 'pip')
|
|
207
|
-
resolvedInput = pip12;
|
|
230
|
+
.replace(/^(python3|python)$/, py12);
|
|
208
231
|
}
|
|
209
|
-
//
|
|
232
|
+
// Ensure workspace directory
|
|
233
|
+
const cwd = ensureWorkspace();
|
|
234
|
+
// Route to Docker sandbox if available, otherwise system shell (in workspace)
|
|
210
235
|
processing = true;
|
|
211
236
|
try {
|
|
212
237
|
if (isDockerAvailable()) {
|
|
@@ -215,11 +240,11 @@ export async function startRepl(program, resumeMode) {
|
|
|
215
240
|
await runInSandbox(resolvedInput, rl);
|
|
216
241
|
}
|
|
217
242
|
else {
|
|
218
|
-
await runSystemCommand(resolvedInput, rl);
|
|
243
|
+
await runSystemCommand(resolvedInput, rl, cwd);
|
|
219
244
|
}
|
|
220
245
|
}
|
|
221
246
|
else {
|
|
222
|
-
await runSystemCommand(resolvedInput, rl);
|
|
247
|
+
await runSystemCommand(resolvedInput, rl, cwd);
|
|
223
248
|
}
|
|
224
249
|
}
|
|
225
250
|
catch {
|
|
@@ -264,13 +289,13 @@ export async function startRepl(program, resumeMode) {
|
|
|
264
289
|
realExit(0);
|
|
265
290
|
});
|
|
266
291
|
}
|
|
267
|
-
function runSystemCommand(input, rl) {
|
|
292
|
+
function runSystemCommand(input, rl, cwd) {
|
|
268
293
|
return new Promise((resolve) => {
|
|
269
|
-
// Pause readline so the child process gets full terminal control
|
|
270
294
|
rl.pause();
|
|
271
295
|
const child = spawn(input, {
|
|
272
296
|
shell: true,
|
|
273
297
|
stdio: 'inherit',
|
|
298
|
+
cwd: cwd || process.cwd(),
|
|
274
299
|
});
|
|
275
300
|
child.on('close', () => {
|
|
276
301
|
rl.resume();
|