From d28f14aa11dad81c94d6487446f1af3fc4d943c6 Mon Sep 17 00:00:00 2001 From: Agando Date: Tue, 29 Sep 2026 13:11:27 +0200 Subject: newOS4: Ctrl+C/Esc stops tail -f follow; security whitelist gains sed --- modules/core/terminal.js | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'modules/core/terminal.js') diff --git a/modules/core/terminal.js b/modules/core/terminal.js index 19a64e2..f322446 100644 --- a/modules/core/terminal.js +++ b/modules/core/terminal.js @@ -10,7 +10,7 @@ this._baseDestructive = ['delete','del','write','save','overwrite','format','kill','stop','shutdown','reboot']; this._baseSystem = ['reboot','shutdown','format','kill','halt','poweroff']; this._baseSuspiciousFlags = ['--dump','--execute','--eval','--exec','--shell','--script']; - this._baseWhitelist = ['help','clear','ls','cat','pwd','whoami','df','free','top','ps','echo','date','env','history','find','grep','tree','head','tail','wc','sort','du','diff','which','hostname','cal','basename','dirname','yes','seq','cut','uniq','expand','printf','base64','sleep','ll','h','mem','status','health-check','system-health','alias','unalias','modules','listmods','modules-list','nvfs-index','nvfs-status','nvfs-resolve','nvfs-tree','nvfs-config','nvfs-alias','nvfs-sync','floating-add','floating-remove','floating-list','floating-pos','floating-clear','floating-toggle','mem-debug','reset-mem','reset','factory-reset','reset-vfs','js','js-run','node','debug','debug-ai','debug-stats','debug-history','debug-analyze','debug-trends','debug-fix','debug-health','session-new','orchestrate','orchestrate-parallel','orchestrate-decompose','orchestrate-result','orchestrator-status','orchestrator-spawn','orchestrator-kill','orchestrator-purge','orchestrator-config','orchestrator-list','orchestrator-sleep','orchestrator-wake','orchestrator-restart-policy','a2a-send','a2a-list','a2a-post','a2a-poll','a2a-ack','a2a-reject','a2a-status','a2a-prune','consensus','consensus-status','consensus-history','pipeline','agent-list','agent-register','agent-status','agent-unregister','contract-list','contract-register','voice','speak','analyze','dashboard','context-auto']; + this._baseWhitelist = ['help','clear','ls','cat','pwd','whoami','df','free','top','ps','echo','date','env','history','find','grep','tree','head','tail','sed','wc','sort','du','diff','which','hostname','cal','basename','dirname','yes','seq','cut','uniq','expand','printf','base64','sleep','ll','h','mem','status','health-check','system-health','alias','unalias','modules','listmods','modules-list','nvfs-index','nvfs-status','nvfs-resolve','nvfs-tree','nvfs-config','nvfs-alias','nvfs-sync','floating-add','floating-remove','floating-list','floating-pos','floating-clear','floating-toggle','mem-debug','reset-mem','reset','factory-reset','reset-vfs','js','js-run','node','debug','debug-ai','debug-stats','debug-history','debug-analyze','debug-trends','debug-fix','debug-health','session-new','orchestrate','orchestrate-parallel','orchestrate-decompose','orchestrate-result','orchestrator-status','orchestrator-spawn','orchestrator-kill','orchestrator-purge','orchestrator-config','orchestrator-list','orchestrator-sleep','orchestrator-wake','orchestrator-restart-policy','a2a-send','a2a-list','a2a-post','a2a-poll','a2a-ack','a2a-reject','a2a-status','a2a-prune','consensus','consensus-status','consensus-history','pipeline','agent-list','agent-register','agent-status','agent-unregister','contract-list','contract-register','voice','speak','analyze','dashboard','context-auto']; this.destructiveCmds = new Set(this._baseDestructive); this.systemCmds = new Set(this._baseSystem); this.suspiciousFlags = new Set(this._baseSuspiciousFlags); @@ -293,6 +293,7 @@ this.registerConfigItem('terminal-sysmsg', true, (v) => { this._showSystemMessages = !!v; }); this._pipeInput = null; this._pipeBufferSize = 5000; + this._activeFollow = null; this._historyBuffer = []; this._historyMaxLines = 50; this._historySavePending = false; @@ -350,6 +351,15 @@ _invalidateCommandCache() { if (this.security) this.security.invalidateCommandCache(); } + stopFollow(msg) { + if (!this._activeFollow) return false; + clearInterval(this._activeFollow.interval); + const f = this._activeFollow.file; + this._activeFollow = null; + if (msg) this.print(msg, 'warning'); + else this.print('^C — stopped following ' + f, 'warning'); + return true; + } initEventListeners() { this.inputField.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { @@ -412,13 +422,16 @@ // Ctrl+L — clear output (standard terminal) e.preventDefault(); this.clearOutput(); - } else if (e.ctrlKey && (e.key === 'c' || e.key === 'C') && this.inputField.value) { - // Ctrl+C — cancel current input line (standard terminal) + } else if (e.ctrlKey && (e.key === 'c' || e.key === 'C')) { + // Ctrl+C — stop tail -f if active, else cancel current input line (standard terminal) e.preventDefault(); - this.print('^C', 'warning'); - this.inputField.value = ''; - this.inputField.style.height = 'auto'; - this.hideAutocomplete(); + if (this.stopFollow()) return; + if (this.inputField.value) { + this.print('^C', 'warning'); + this.inputField.value = ''; + this.inputField.style.height = 'auto'; + this.hideAutocomplete(); + } return; } else if (e.ctrlKey && (e.key === 'u' || e.key === 'U')) { // Ctrl+U — clear line from cursor to start (standard terminal) @@ -428,6 +441,8 @@ } else if (e.key === 'Escape') { if (this._aiBusy) { this.executeCommand('ai-stop'); + } else if (this.stopFollow()) { + // tail -f stopped } else { this.hideAutocomplete(); } -- cgit v1.2.3