From fba6df22dae297c89fa0df065628dddded66c69c Mon Sep 17 00:00:00 2001 From: Agando Date: Thu, 18 Jun 2026 06:00:40 +0200 Subject: auto: 2026-06-18_06:00 changes --- modules/core/core-bundle.js | 727 ++++++++++++++------------------------------ 1 file changed, 227 insertions(+), 500 deletions(-) (limited to 'modules/core/core-bundle.js') diff --git a/modules/core/core-bundle.js b/modules/core/core-bundle.js index 29d6fc8..cb3471a 100644 --- a/modules/core/core-bundle.js +++ b/modules/core/core-bundle.js @@ -933,8 +933,23 @@ window.VirtualFileSystem = VirtualFileSystem; this.terminal = terminal; this.modules = {}; this.moduleRegistry = []; + this._timers = []; // v4.5.68: central timer registry this.loadFromStorage(); } + // v4.5.68: Central timer registry — all modules should use these instead of raw setInterval + _setInterval(fn, ms) { + const id = setInterval(fn, ms); + this._timers.push(id); + return id; + } + _clearTimer(id) { + clearInterval(id); + this._timers = this._timers.filter(t => t !== id); + } + clearAllTimers() { + this._timers.forEach(id => clearInterval(id)); + this._timers = []; + } loadFromStorage() { try { const saved = localStorage.getItem('webx11_modules'); @@ -1101,7 +1116,7 @@ class OllamaManager { this.responseHooks = []; this._abortController = null; // --- AI Agent Loop Optimization Toggle --- - this.optimizationEnabled = localStorage.getItem('webx11_optimization_enabled') !== 'false'; + this.optimizationEnabled = localStorage.getItem('webx11_optimization_enabled') === 'true'; // v4.5.66: default OFF // --- Streaming Toggle --- this.streamEnabled = localStorage.getItem('webx11_stream_enabled') === 'true'; // --- Validator Agent Config --- @@ -1434,6 +1449,21 @@ class OllamaManager { return false; } + // v4.5.68: Fetch with timeout — wraps fetch() with AbortController to prevent network hangs + async fetchWithTimeout(url, options = {}, timeoutMs = 30000) { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), timeoutMs); + try { + const resp = await fetch(url, { ...options, signal: controller.signal }); + clearTimeout(timer); + return resp; + } catch (e) { + clearTimeout(timer); + if (e.name === 'AbortError') throw new Error(`Request timed out after ${timeoutMs / 1000}s`); + throw e; + } + } + // ─── Chat (non-streaming) ──────────────────────────────────────────────── async chat(prompt, model = null) { @@ -2207,7 +2237,7 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm 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','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']); + 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','reset','factory-reset','reset-vfs','js','js-run','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){} @@ -3560,6 +3590,20 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm const parts = this.security._parseArgs(cmdText); const cmd = parts[0].toLowerCase(); const args = parts.slice(1); + // v4.5.67: Security gate for pipe stages — was completely bypassed + const cls = this.security._classify(cmdText); + if (cls.destructive && this.security.level > 0) { + this.print(`⚠️ Security: Destructive command "${cmd}" blocked in pipe at level ${this.security.level}`, 'error'); + return; + } + if (cls.system && this.security.level > 0) { + this.print(`⚠️ Security: System command "${cmd}" blocked in pipe at level ${this.security.level}`, 'error'); + return; + } + if (!cls.known && this.security.level >= 2) { + this.print(`⚠️ Security: Unknown command "${cmd}" blocked in pipe (paranoid mode)`, 'error'); + return; + } this._pipeInput = pipeInput; if (i < pipeParts.length - 1) { pipeInput = await this.capturePipeOutput(cmd, args); @@ -4767,8 +4811,9 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm 'doctor': 'doctor — Run all health checks, rank issues, show top 3 warnings', 'bug-report': 'bug-report — Generate a markdown bug report to /tmp/ for debugging', 'preview': 'preview — Render an HTML file from the VFS in a preview pane', - 'factory-reset': 'factory-reset [--confirm] — Wipe all webx11_ data (VFS, config, AI profile, mindmaps, themes, all localStorage)', - 'reset-vfs': 'reset-vfs [--confirm] — Clear virtual filesystem and auto-session backups (keeps settings, modules, AI profile)', + 'reset': 'reset [mem|vfs|config|all] — Global reset command (default: mem)', + 'factory-reset': 'factory-reset — Alias for reset all', + 'reset-vfs': 'reset-vfs — Alias for reset vfs', 'logs': 'logs [n] [LEVEL] — Show last n log entries (optionally filtered by level: INFO/WARN/ERROR)', 'logrotate': 'logrotate — Rotate current.log to a dated archive (auto-cleans old)', 'health-check': 'health-check — Audit VFS size, modules, logs, trash, skills, tool failures', @@ -5056,7 +5101,13 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm }; this.commands['preview'] = (args) => this.terminal.openPreview(args); - this.commands['factory-reset'] = (args) => { + + // v4.5.64: reset — unified reset command superseding factory-reset, reset-vfs, reset-mem, config-reset + this.commands['reset'] = (args) => this._globalReset(args); + this.commands['factory-reset'] = (args) => this._globalReset(['all', ...args]); + this.commands['reset-vfs'] = (args) => this._globalReset(['vfs', ...args]); + + this.commands['factory-reset-old'] = (args) => { if (!args[0] || args[0] !== '--confirm') { this.terminal.print('⚠️ FACTORY RESET will clear all data:', 'error'); this.terminal.print(' • Filesystem (all files + directories)', 'warning'); @@ -5199,6 +5250,122 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm } }; } + + // v4.5.64: Unified reset — subcommands: mem, vfs, config, all + _globalReset(args) { + const scope = (args[0] || 'mem').toLowerCase(); + const confirmed = args.includes('--confirm') || args.includes('-y'); + + const scopes = { + mem: { + title: 'AI MEMORY', + items: ['AI conversation context', 'AI patterns & guardrails', 'AI user profile', 'AI system prompt', 'VaultMind token', 'AI internal logs (tool calls, heap stats)'], + keys: ['webx11_ai_context', 'webx11_ai_config', 'webx11_ai_patterns', 'webx11_ai_system_prompt', 'webx11_user_profile', 'webx11_guardrail_policy', 'vaultmind_token'], + desc: 'Clears AI state only. VFS, settings, modules, and config are untouched.' + }, + vfs: { + title: 'VIRTUAL FILESYSTEM', + items: ['All files and directories', 'Session backups', 'VFS metadata', 'Neural VFS index'], + desc: 'Clears the VFS to empty root. Settings, modules, AI profile, and config are preserved.' + }, + config: { + title: 'CONFIGURATION', + items: ['All webx11_config_* keys', 'Ollama endpoint & model', 'Security settings', 'Terminal config', 'Font size', 'Theme'], + keys: null, // prefix-based wipe + desc: 'Resets all configuration to defaults. VFS and AI memory are untouched.' + }, + all: { + title: 'EVERYTHING', + items: ['VFS (all files)', 'AI memory & profile', 'All config & settings', 'Module registry & disabled list', 'Terminal history', 'Themes', 'Chat settings', 'Cron jobs', 'Floating elements', 'Aliases', 'Tutorial state', 'Server list', 'Orchestrator config', 'Secret vault keys', 'Neural VFS config'], + desc: 'Complete wipe. The system will reload as if first visit.' + } + }; + + const s = scopes[scope]; + if (!s) { + this.terminal.print(`Usage: reset [mem|vfs|config|all] [--confirm]`, 'warning'); + this.terminal.print(` reset mem — Clear AI memory (default)`, 'info'); + this.terminal.print(` reset vfs — Clear virtual filesystem`, 'info'); + this.terminal.print(` reset config — Reset all config to defaults`, 'info'); + this.terminal.print(` reset all — Wipe everything (nuclear)`, 'info'); + this.terminal.print(``, 'info'); + this.terminal.print(`Aliases: factory-reset = reset all, reset-vfs = reset vfs`, 'info'); + return; + } + + if (!confirmed) { + this.terminal.print(`⚠️ RESET ${s.title} will clear:`, 'error'); + s.items.forEach(item => this.terminal.print(` • ${item}`, 'warning')); + this.terminal.print(``, 'info'); + this.terminal.print(s.desc, 'info'); + this.terminal.print(``, 'info'); + this.terminal.print(`Type: reset ${scope} --confirm`, 'error'); + return; + } + + // Execute reset + if (scope === 'mem') { + this._resetMem(); + } else if (scope === 'vfs') { + this._resetVfs(); + } else if (scope === 'config') { + this._resetConfig(); + } else if (scope === 'all') { + this._resetAll(); + } + + localStorage.setItem('webx11_last_reset', Date.now().toString()); + this.terminal.print(`✅ ${s.title} reset complete. Reloading...`, 'success'); + setTimeout(() => location.reload(), 1000); + } + + _resetMem() { + const keys = ['webx11_ai_context', 'webx11_ai_config', 'webx11_ai_patterns', 'webx11_ai_system_prompt', 'webx11_user_profile', 'webx11_guardrail_policy', 'vaultmind_token']; + keys.forEach(k => localStorage.removeItem(k)); + // Also clear AI logs in VFS + try { this.terminal.fs.deleteFile('/logs/ai-tool-log.json'); } catch (e) {} + } + + _resetVfs() { + this.terminal.fs.root = { type: 'dir', content: {} }; + this.terminal.fs.currentPath = []; + try { + this.terminal.fs.deleteFile('_sessions/_auto.json'); + this.terminal.fs.deleteFile('_sessions/_auto-context.json'); + } catch (e) {} + this.terminal.fs.saveToStorage(); + } + + _resetConfig() { + // Wipe all webx11_config_* keys + const toRemove = []; + for (let i = localStorage.length - 1; i >= 0; i--) { + const key = localStorage.key(i); + if (key && (key.startsWith('webx11_config_') || key === 'webx11_ollama_endpoint' || key === 'webx11_ollama_model' || key.startsWith('webx11_security_') || key === 'webx11_terminal_config' || key === 'webx11_font_size' || key === 'webx11_active_theme' || key === 'webx11_themes' || key === 'webx11_model_params' || key.startsWith('webx11_validator_') || key === 'webx11_optimization_enabled' || key === 'webx11_stream_enabled' || key === 'webx11_smart_prompt' || key === 'webx11_config_ai_sound' || key === 'webx11_welcome_dismissed')) { + toRemove.push(key); + } + } + toRemove.forEach(k => localStorage.removeItem(k)); + } + + _resetAll() { + // Delete session backups so they don't resurrect + try { + this.terminal.fs.deleteFile('_sessions/_auto.json'); + this.terminal.fs.deleteFile('_sessions/_auto-context.json'); + this.terminal.fs.reset(); + } catch (e) { console.warn('reset all FS cleanup:', e); } + // Wipe ALL webx11_ prefixed keys + for (let i = localStorage.length - 1; i >= 0; i--) { + const key = localStorage.key(i); + if (key && key.startsWith('webx11_')) localStorage.removeItem(key); + } + // Also wipe non-prefixed keys used by modules + const extraKeys = ['vaultmind_token', 'cslite_messages', 'webx11_chat_settings', 'webx11_chat_logs', 'webx11_chat_api']; + extraKeys.forEach(k => localStorage.removeItem(k)); + this.terminal.outputContainer.innerHTML = ''; + } + _autoRotateLogs(force = false) { try { const content = this.terminal.fs.readFile('/logs/current.log') || ''; @@ -5321,7 +5488,7 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm FileSystemModule: '📁 Filesystem', WebX11AIAssistantModule: '🤖 AI', WebXAIAliasModule: '🌐 WebX', - DuckDuckGoModule: '🔍 Search & Web', + SearXNGModule: '🔍 Search & Web', WebFetcherModule: '🔍 Search & Web', AliasModule: '🔧 Tools', @@ -5346,18 +5513,18 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm NeuralVFSModule: '🧠 Neural VFS', SkillPackCoreModule: '📚 Skills', ConfigManagerModule: '⚙️ Config', - ModuleIOModule: '🔌 I/O', + SandboxModule: '🏖️ Sandbox', - MyModule: '💾 Data', + WebDAVModule: '🧰 Extras', ChatStudioModule: '🧰 Extras', SimpleGUIModule: '🖥️ GUI', SecretVault: '🔒 Vault', OrchestratorModule: '🧠 Orchestrator', PredictorModule: '🔮 Prediction', - SmartContextModule: '🧠 Context', + MemoryGraphModule: '📊 Memory', - DynamicPromptsModule: '💬 Prompts', + WorldModelModule: '🌍 World', LeaseModule: '🔐 Leases', AgentRegistryModule: '🤖 Agents', @@ -6118,90 +6285,6 @@ class OllamaModule { } } -/* === module-my.js === */ -class MyModule { - constructor() { - this.commands = {}; this.terminal = null; this.userData = {}; - try { const d = localStorage.getItem('webx11_my_data'); if (d) this.userData = JSON.parse(d); } catch (e) {} - } - getHelpInfo() { - return { - 'my': 'my — Show module info and stored key count', - 'my-set': 'my-set — Store a key-value pair', - 'my-get': 'my-get — Retrieve a stored value by key', - 'my-list': 'my-list — List all stored keys', - 'my-export': 'my-export — Export all stored data as JSON', - 'my-import': 'my-import — Import data from JSON string', - 'my-clear': 'my-clear — Clear all stored data' - }; - } - _save() { - try { localStorage.setItem('webx11_my_data', JSON.stringify(this.userData)); } catch (e) {} - } - registerCommands() { - this.commands['my'] = () => this.showInfo(); - this.commands['my-set'] = (args) => this.setData(args); - this.commands['my-get'] = (args) => this.getData(args); - this.commands['my-list'] = () => this.listData(); - this.commands['my-export'] = () => this.exportData(); - this.commands['my-import'] = (args) => this.importData(args); - this.commands['my-clear'] = () => this.clearData(); - } - showInfo() { - this.terminal.print("=== My Module v2.0 ===", "info"); - this.terminal.print("Personal workspace for custom commands"); - this.terminal.print(`Stored keys: ${Object.keys(this.userData).length}`, "success"); - } - setData(args) { - if (args.length < 2) { - this.terminal.print("Usage: my-set ", "warning"); return; - } - const key = args[0], value = args.slice(1).join(' '); - this.userData[key] = { value, timestamp: new Date().toISOString() }; - this._save(); - this.terminal.print(`Saved: ${key} = ${value}`, "success"); - } - getData(args) { - if (!args[0]) { - this.terminal.print("Usage: my-get ", "warning"); return; - } - if (this.userData[args[0]]) { - this.terminal.print(`${args[0]}: ${this.userData[args[0]].value}`, "info"); - } else { - this.terminal.print(`Key '${args[0]}' not found`, "error"); - } - } - listData() { - const keys = Object.keys(this.userData); - if (!keys.length) return this.terminal.print('No data stored.', 'info'); - this.terminal.print('=== My Data ===', 'info'); - keys.forEach(k => { - const v = this.userData[k]; - const type = typeof v.value; - this.terminal.print(` ${k} (${type}): ${String(v.value).substring(0, 40)}`); - }); - } - exportData() { - this.terminal.print(JSON.stringify(this.userData, null, 2)); - } - importData(args) { - if (!args[0]) return this.terminal.print('Usage: my-import ', 'warning'); - try { - const data = JSON.parse(args.join(' ')); - Object.assign(this.userData, data); - this._save(); - this.terminal.print(`Imported ${Object.keys(data).length} keys`, 'success'); - } catch (e) { - this.terminal.print('Invalid JSON: ' + e.message, 'error'); - } - } - clearData() { - this.userData = {}; - localStorage.removeItem('webx11_my_data'); - this.terminal.print("Personal data cleared", "success"); - } -} - /* === module-modmgr.js === */ // Extracted from newOS4 index.html — ModuleManagerModule // v4.5.43: Enhanced with module-info, module-list, module-stats, module-disable/enable, @@ -6781,9 +6864,10 @@ class ServerMonitorModule { if (this._latencyLog.length > this._maxLatencyEntries) { this._latencyLog = this._latencyLog.slice(-this._maxLatencyEntries); } - // Persist every 20 calls - if (this._latencyLog.length % 20 === 0) { + // v4.5.66: Throttled persistence — every 100 calls or 5 minutes (was every 20) + if (this._latencyLog.length % 100 === 0 || (Date.now() - (this._lastPersistTs || 0)) > 300000) { this._persistLatency(); + this._lastPersistTs = Date.now(); } } _persistLatency() { @@ -7086,13 +7170,22 @@ class ServerMonitorModule { } const label = args[0], command = args.slice(1).join(' '); const id = 'gui-btn-' + Date.now(); - const safeCmd = command.replace(/'/g, "\\'"); + // v4.5.68: Use addEventListener instead of onclick attribute to prevent quote-breaking XSS this.terminal.printHTML(`
- +
`); + const btn = document.getElementById(id); + if (btn) { + btn.addEventListener('click', () => { + (window.webx11 || window).executeCommand(command); + }); + } this.guiElements[id] = { type: 'button', label, command }; this.terminal.print(`Button '${label}' created`, "success"); } + _escapeHtml(str) { + return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); + } createInput(args) { if (args.length < 2) { this.terminal.print("Usage: gui-input ", "warning"); return; @@ -7124,192 +7217,6 @@ class ServerMonitorModule { } } -/* === module-io.js === */ -// Extracted from newOS4 index.html — ModuleIOModule - class ModuleIOModule { - constructor() { this.commands = {}; this.terminal = null; } - getHelpInfo() { - return { - 'export-mods': 'export-mods — Export all loaded modules to a JSON file', - 'import-mods': 'import-mods — Import modules from a JSON file', - 'backup': 'backup — Create a full system backup', - 'restore': 'restore — Restore system from a backup file', - 'share-mod': 'share-mod — Share a module as JSON to clipboard' - }; - } - registerCommands() { - this.commands['export-mods'] = () => this.exportModules(); - this.commands['import-mods'] = (args) => this.importModules(args); - this.commands['backup'] = () => this.fullBackup(); - this.commands['restore'] = (args) => this.restoreBackup(args); - this.commands['share-mod'] = (args) => this.shareModule(args); - } - exportModules() { - const registry = this.terminal.moduleManager.moduleRegistry; - if (registry.length === 0) { - this.terminal.print("No modules to export", "warning"); return; - } - const exportData = { version: '4.1', exported: new Date().toISOString(), modules: registry }; - const json = JSON.stringify(exportData, null, 2); - const blob = new Blob([json], { type: 'application/json' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; a.download = `webx11-modules-${Date.now()}.json`; a.click(); - URL.revokeObjectURL(url); - this.terminal.print(`Exported ${registry.length} module(s)`, "success"); - } - importModules(args) { - this.terminal.print("⚠️ To import modules:", "warning"); - this.terminal.print("1. Upload the JSON file using 'upload'", "info"); - this.terminal.print("2. Use: import-mods ", "info"); - } - async fullBackup() { - const backup = { - version: '4.1', timestamp: new Date().toISOString(), - filesystem: localStorage.getItem('webx11_filesystem'), - modules: localStorage.getItem('webx11_modules'), - ollama: { - endpoint: localStorage.getItem('webx11_ollama_endpoint'), - model: localStorage.getItem('webx11_ollama_model') - }, config: {} - }; - for (let i = 0; i < localStorage.length; i++) { - const key = localStorage.key(i); - if (key.startsWith('webx11_')) { backup.config[key] = localStorage.getItem(key); } - } - const json = JSON.stringify(backup, null, 2); - const blob = new Blob([json], { type: 'application/json' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; a.download = `webx11-backup-${Date.now()}.json`; a.click(); - URL.revokeObjectURL(url); - this.terminal.print("Full backup created", "success"); - } - restoreBackup(args) { - this.terminal.print("⚠️ To restore backup:", "warning"); - this.terminal.print("1. Upload the backup JSON file", "info"); - this.terminal.print("2. Use: restore ", "info"); - } - shareModule(args) { - if (!args[0]) { - this.terminal.print("Usage: share-mod ", "warning"); return; - } - const mod = this.terminal.moduleManager.moduleRegistry.find(m => m.name === args[0]); - if (!mod) { - this.terminal.print(`Module '${args[0]}' not found`, "error"); return; - } - const shareData = { name: mod.name, url: mod.url, shared: new Date().toISOString() }; - const json = JSON.stringify(shareData, null, 2); - navigator.clipboard.writeText(json).then(() => { - this.terminal.print("Module info copied to clipboard", "success"); - }).catch(() => { this.terminal.print(json, "info"); }); - } - } - -/* === module-dynamicprompts.js === */ -// Extracted from newOS4 index.html — DynamicPromptsModule - class DynamicPromptsModule { - constructor() { - this.commands = {}; this.terminal = null; - this.prompts = this.loadPrompts(); this.variables = {}; - } - getHelpInfo() { - return { - 'prompt': 'prompt [additional-text] — Use a saved prompt template', - 'prompt-save': 'prompt-save