aboutsummaryrefslogtreecommitdiff
path: root/modules/core/terminal.js
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-06-17 06:01:20 +0200
committerAgando <roduccomm@gmail.com>2026-06-17 06:01:20 +0200
commitcebacc27a48168e666679018f8e27156d12d4527 (patch)
treeb306d33b67b9d8c84bd9b9c16b1e91ee217628a6 /modules/core/terminal.js
parent6aa24b976952a698c673d350977721b27193183d (diff)
auto: 2026-06-17_06:01 changes
Diffstat (limited to 'modules/core/terminal.js')
-rw-r--r--modules/core/terminal.js97
1 files changed, 58 insertions, 39 deletions
diff --git a/modules/core/terminal.js b/modules/core/terminal.js
index 504e08d..05aefef 100644
--- a/modules/core/terminal.js
+++ b/modules/core/terminal.js
@@ -10,7 +10,7 @@
this.destructiveCmds = new Set(['delete','del','write','save','overwrite','format','kill','stop','shutdown','reboot']);
this.systemCmds = new Set(['reboot','shutdown','format','kill','halt','poweroff']);
this.suspiciousFlags = new Set(['--dump','--execute','--eval','--exec','--shell','--script']);
- this.whitelist = new Set(['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']);
+ this.whitelist = new Set(['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','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']);
// Load persisted overrides from localStorage
try { const d = localStorage.getItem('webx11_security_destructive'); if(d){JSON.parse(d).forEach(c=>this.destructiveCmds.add(c));} }catch(e){}
try { const s = localStorage.getItem('webx11_security_system'); if(s){JSON.parse(s).forEach(c=>this.systemCmds.add(c));} }catch(e){}
@@ -19,7 +19,7 @@
this._registerBuiltIn();
}
_registerBuiltIn() {
- const internals = ['clear','help','ls','cat','df','free','env','history','cd','pwd','touch','mkdir','rm','cp','mv','edit','cat','run','font-size','load-local','loadlocal','mem','security','security-level','security-destructive','security-system','security-whitelist','security-flag','config','ai-config','startup-modules','smart-prompt','cmd-allow','cmd-deny','system-health','server','servers','server-check','monitor','vfs','vfs-clean','vfs-size','empty-trash','expand','sysmsg'];
+ const internals = ['clear','help','ls','cat','df','free','env','history','cd','pwd','touch','mkdir','rm','cp','mv','edit','cat','run','font-size','load-local','loadlocal','mem','security','security-level','security-destructive','security-system','security-whitelist','security-flag','config','ai-config','startup-modules','smart-prompt','cmd-allow','cmd-deny','system-health','server','servers','server-check','monitor','vfs','vfs-clean','vfs-size','empty-trash','expand','sysmsg','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'];
internals.forEach(c => this.registerCommand(c, { category: 'builtin' }));
}
setLevel(n) {
@@ -854,35 +854,14 @@
_autoSaveSession() {
try {
this.fs.mkdirRecursive('_sessions');
- var rootClone;
- try {
- const json = JSON.stringify(this.fs.root);
- if (json.length > 52428800) { // 50MB cap
- rootClone = { type: 'directory', name: '/', content: {}, _truncated: true };
- this.print('⚠ Auto-save skipped (VFS too large)', 'warning');
- return;
- }
- rootClone = JSON.parse(json);
- } catch (e) {
- rootClone = { type: 'directory', name: '/', content: {}, _error: true };
- return;
- }
- if (rootClone && rootClone.content && rootClone.content['_sessions']) {
- delete rootClone.content['_sessions'];
- }
const snapshot = {
type: 'session',
+ version: 2,
savedAt: new Date().toISOString(),
- fsRoot: rootClone,
currentPath: this.fs.currentPath,
- version: 1,
terminalLines: this._historyBuffer.slice(-50)
};
const snapshotJson = JSON.stringify(snapshot);
- if (snapshotJson.length > 52428800) {
- this.print('⚠ Auto-save snapshot too large, skipping', 'warning');
- return;
- }
this.fs.writeFile('_sessions/_auto.json', snapshotJson);
try {
const ctx = localStorage.getItem('webx11_ai_context');
@@ -907,19 +886,40 @@
this.print('↻ Skipped stale auto-session backup (older than last reset)', 'info');
return;
}
- if (data && data.fsRoot) {
+ if (data.version === 2) {
+ // v2: metadata-only restore (VFS is persisted independently in localStorage)
+ this.fs.currentPath = data.currentPath || [];
+ this.fs.saveToStorage();
+ this.updatePrompt();
+ this.print('↻ Auto-restored session from ' + (data.savedAt ? new Date(data.savedAt).toLocaleTimeString() : 'recent'), 'success');
+ // Optionally re-render last few terminal lines
+ if (data.terminalLines && Array.isArray(data.terminalLines)) {
+ data.terminalLines.slice(-10).forEach(line => {
+ const text = typeof line === 'string' ? line : (line.l || '');
+ this.print(text, typeof line === 'object' ? (line.t || '') : '');
+ });
+ }
+ try {
+ const aiCtx = this.fs.readFile('_sessions/_auto-context.json');
+ if (aiCtx) {
+ localStorage.setItem('webx11_ai_context', aiCtx);
+ }
+ } catch (e) {}
+ this.fs.deleteFile('_sessions/_auto.json');
+ this.fs.deleteFile('_sessions/_auto-context.json');
+ } else if (data && data.fsRoot) {
+ // v1: full VFS restore (legacy)
this.fs.root = data.fsRoot;
this.fs.currentPath = data.currentPath || [];
this.fs.saveToStorage();
+ this.updatePrompt();
this.print('↻ Auto-restored session from ' + (data.savedAt ? new Date(data.savedAt).toLocaleTimeString() : 'recent'), 'success');
- // Defer AI context restoration (AI module loads after terminal init)
try {
const aiCtx = this.fs.readFile('_sessions/_auto-context.json');
if (aiCtx) {
localStorage.setItem('webx11_ai_context', aiCtx);
}
} catch (e) {}
- // v4.5.47: delete backup after successful restore so reset-mem doesn't get resurrected
this.fs.deleteFile('_sessions/_auto.json');
this.fs.deleteFile('_sessions/_auto-context.json');
}
@@ -1566,22 +1566,41 @@
return;
}
if (cmd === 'history') {
- const search = args.join(' ').toLowerCase();
const entries = this.history;
if (!entries.length) { this.print('No command history.', 'info'); return; }
- if (search) {
- const filtered = entries.map((c, i) => ({ i, c })).filter(e => e.c.toLowerCase().includes(search));
- if (!filtered.length) { this.print(`No history entries matching: ${search}`, 'info'); return; }
- this.print(`=== History (matching: ${search}) ===`, 'info');
- filtered.forEach(e => this.print(` ${e.i + 1} ${e.c}`));
- } else {
- this.print(`=== Command History (${entries.length} entries) ===`, 'info');
- const start = Math.max(0, entries.length - 50);
- for (let i = start; i < entries.length; i++) {
- this.print(` ${i + 1} ${entries[i]}`);
+ // Flags
+ if (args[0] === '--clear' || args[0] === '-c') {
+ this.history = [];
+ this.historyIndex = 0;
+ localStorage.removeItem('webx11_command_history');
+ this.print('History cleared.', 'success');
+ return;
+ }
+ if (args[0] === '--export' || args[0] === '-e') {
+ try {
+ this.fs.mkdirRecursive('system');
+ this.fs.writeFile('system/history.json', JSON.stringify(entries));
+ this.print('History exported to /system/history.json (' + entries.length + ' entries).', 'success');
+ } catch (e) {
+ this.print('Export failed: ' + e.message, 'error');
}
- if (entries.length > 50) this.print(`... (${entries.length - 50} older entries hidden, use history <search> to find)`);
+ return;
+ }
+ if (args[0] === '--search' || args[0] === '-s') {
+ const term = args.slice(1).join(' ').toLowerCase();
+ if (!term) { this.print('Usage: history --search <term>', 'warning'); return; }
+ const filtered = entries.filter(e => e.toLowerCase().includes(term));
+ if (!filtered.length) { this.print('No matches for: ' + term, 'info'); return; }
+ this.print('=== History (matching: ' + term + ') ===', 'info');
+ filtered.forEach(e => this.print(' ' + e));
+ return;
}
+ let count = 50;
+ if (args[0] === '-n' && args[1]) count = parseInt(args[1]) || 50;
+ const slice = entries.slice(-count);
+ this.print('=== Command History (' + entries.length + ' entries) ===', 'info');
+ slice.forEach(c => this.print(' ' + c));
+ if (entries.length > count) this.print('... (' + (entries.length - count) + ' older, use history --search <term> to find)');
return;
}
for (const modName in this.moduleManager.modules) {