aboutsummaryrefslogtreecommitdiff
path: root/modules/core/terminal.js
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-09-29 13:11:27 +0200
committerAgando <roduccomm@gmail.com>2026-09-29 13:11:27 +0200
commitd28f14aa11dad81c94d6487446f1af3fc4d943c6 (patch)
tree233d7b93b284b34cec1e1a75d9ed5722cb3c0c2d /modules/core/terminal.js
parenta96ee68a868800d23adaed58be1763a702ff4b26 (diff)
newOS4: Ctrl+C/Esc stops tail -f follow; security whitelist gains sed
Diffstat (limited to 'modules/core/terminal.js')
-rw-r--r--modules/core/terminal.js29
1 files changed, 22 insertions, 7 deletions
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();
}