diff options
Diffstat (limited to 'modules/core/core-bundle.js')
| -rw-r--r-- | modules/core/core-bundle.js | 74 |
1 files changed, 60 insertions, 14 deletions
diff --git a/modules/core/core-bundle.js b/modules/core/core-bundle.js index c654021..941707e 100644 --- a/modules/core/core-bundle.js +++ b/modules/core/core-bundle.js @@ -2628,7 +2628,33 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm const unknown = !known; return { cmd, args, known, destructive, system, unknown, hasSuspicious }; } + // v4.6.58: this terminal has no shell. `;` at least failed loudly, but `&&`/`||` + // folded the right-hand side into the left command's argv, and a `|` arriving from + // the AI run_command tool (which bypasses splitPipe) degraded to an empty pattern + // matching every line. Both are silent wrong answers — refuse instead (packet §1). + detectComposition(input) { + const s = String(input || ''); + let inSingle = false, inDouble = false; + for (let i = 0; i < s.length; i++) { + const ch = s[i]; + if (ch === '"' && !inSingle) { inDouble = !inDouble; continue; } + if (ch === "'" && !inDouble) { inSingle = !inSingle; continue; } + if (inSingle || inDouble) continue; + if (ch === '&' && s[i + 1] === '&') return '&&'; + if (ch === '|' && s[i + 1] === '|') return '||'; + if (ch === '|') return '|'; + if (ch === ';') return ';'; + } + return null; + } async checkAndRun(rawInput) { + const badOp = this.detectComposition(rawInput); + if (badOp) { + this.terminal.print('⚠️ No shell here — `' + badOp + '` is not interpreted. ' + + 'Run one command per line (the interactive terminal supports `cmd | cmd`; ' + + 'AI run_command does not).', 'error'); + return; + } const { cmd, args, known, destructive, system, unknown, hasSuspicious } = this._classify(rawInput); // Level 3 Audit: just log and allow if (this.level === 3) { @@ -6766,26 +6792,46 @@ class FileSystemModule { this.commands['wc'] = (args) => { // 2026-10-21: accept -l / -w / -c (previously ANY flag was read as a filename, // so `wc -l file` answered "wc: -l: No such file"). + // v4.6.58: multiple files. `wc -c a b` used to print only `a` and exit clean — + // a silent false negative (packet §1). const want = { l: false, w: false, c: false }; - let file = null; + const files = []; (args || []).forEach(a => { if (/^-[lwc]+$/.test(a)) a.slice(1).split('').forEach(k => { want[k] = true; }); else if (/^-[a-zA-Z]+$/.test(a)) { /* ignore other flags */ } - else if (!file) file = a; + else files.push(a); }); - if (!file) return this.terminal.print('Usage: wc [-l] [-w] [-c] <filename>', 'warning'); - const content = this.terminal.fs.readFile(file); - if (content === null || content === undefined) return this.terminal.print('wc: ' + file + ': No such file', 'error'); - const lines = content.split('\n').length; - const words = content.split(/\s+/).filter(w => w).length; - const chars = content.length; + if (!files.length) return this.terminal.print('Usage: wc [-l] [-w] [-c] <filename> [...]', 'warning'); const anyFlag = want.l || want.w || want.c; - const out = []; - if (want.l || !anyFlag) out.push(lines); - if (want.w || !anyFlag) out.push(words); - if (want.c || !anyFlag) out.push(chars); - if (anyFlag && !(want.l && want.w && want.c)) out.push(file); - this.terminal.print(out.join(' ')); + const rows = []; + let total = { l: 0, w: 0, c: 0 }; + files.forEach(file => { + const content = this.terminal.fs.readFile(file); + if (content === null || content === undefined) { + this.terminal.print('wc: ' + file + ': No such file', 'error'); + return; + } + const r = { + file: file, + l: content.split('\n').length, + w: content.split(/\s+/).filter(w => w).length, + c: content.length + }; + total.l += r.l; total.w += r.w; total.c += r.c; + rows.push(r); + }); + if (!rows.length) return; + const multi = rows.length > 1; + const fmt = (r, name) => { + const out = []; + if (want.l || !anyFlag) out.push(r.l); + if (want.w || !anyFlag) out.push(r.w); + if (want.c || !anyFlag) out.push(r.c); + if (multi || (anyFlag && !(want.l && want.w && want.c))) out.push(name); + return out.join(' '); + }; + rows.forEach(r => this.terminal.print(fmt(r, r.file))); + if (multi) this.terminal.print(fmt(total, 'total')); }; this.commands['touch'] = (args) => { if(!args[0]) return this.terminal.print('Usage: touch <filename>', 'warning'); |