// Extracted from newOS4 index.html — SystemModule class SystemModule { constructor() { this.commands = {}; this.terminal = null; } getHelpInfo() { return { 'help': 'help [command] — Show all commands or usage for a specific command', 'echo': 'echo — Print text to the terminal', 'mem': 'mem — Show current memory usage', 'status': 'status — Display system status (network, files, modules, Ollama)', '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 only the virtual filesystem (keeps settings, modules, AI profile)', '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', 'memory-map-export': 'memory-map-export — Generate memory map markdown from memory/index.json', 'font-size': 'font-size <8-32> — Set terminal font size (persists across sessions)', 'setfont': 'setfont <8-32> — Alias for font-size', 'smart-prompt': 'smart-prompt [on|off] — Toggle context-aware prompt (shows name, skills, sessions, memory, edits)', 'checkpoint': 'checkpoint [name] — Create a VFS snapshot (name defaults to ISO timestamp)', 'checkpoint-list': 'checkpoint-list — List all VFS checkpoints', 'rollback': 'rollback — Restore VFS from a checkpoint', 'checkpoint-del': 'checkpoint-del — Delete a VFS checkpoint', 'commands': 'commands — List all commands with module source and requirements status', 'check': 'check — Show overall system health including module requirements', }; } registerCommands() { this.commands['help'] = (args) => this.handleHelp(args); this.commands['echo'] = (args) => this.terminal.print(args.join(' ')); this.commands['mem'] = () => { const mem = performance.memory ? (performance.memory.usedJSHeapSize / 1048576).toFixed(2) : 'N/A'; this.terminal.print(`Memory Usage: ${mem} MB`, 'info'); document.getElementById('header-status').innerText = `Mem: ${mem}MB`; }; // Refresh memory display on boot setTimeout(() => { if (performance.memory) { const mem = (performance.memory.usedJSHeapSize / 1048576).toFixed(1); document.getElementById('header-status').innerText = `Mem: ${mem}MB`; } }, 500); this.commands['status'] = () => { this.terminal.print(`System: WebX11 ${window.NEWOS4_VERSION || 'v4.0'}`); this.terminal.print(`Network: ${navigator.onLine ? 'Online' : 'Offline'}`); this.terminal.print(`Files: ${this.terminal.fs.ls().length} items`); this.terminal.print(`Modules: ${Object.keys(this.terminal.moduleManager.modules).length} loaded`); this.terminal.print(`Ollama: ${this.terminal.ollamaManager.connected ? 'Connected' : 'Disconnected'}`); }; this.commands['preview'] = (args) => this.terminal.openPreview(args); this.commands['factory-reset'] = (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'); this.terminal.print(' • AI memory, patterns, user profile', 'warning'); this.terminal.print(' • Ollama endpoint, model selection', 'warning'); this.terminal.print(' • Terminal history, font size', 'warning'); this.terminal.print(' • All config, modules, packages', 'warning'); this.terminal.print(' • Mindmaps, notes, bookmarks', 'warning'); this.terminal.print(' • Themes, context profiles, servers', 'warning'); this.terminal.print(' • Chat settings, my data', 'warning'); this.terminal.print('Type: factory-reset --confirm to proceed', 'error'); return; } for (let i = localStorage.length - 1; i >= 0; i--) { const key = localStorage.key(i); if (key && key.startsWith('webx11_')) localStorage.removeItem(key); } this.terminal.fs.root = { type: 'dir', content: {} }; this.terminal.fs.currentPath = []; this.terminal.outputContainer.innerHTML = ''; this.terminal.print('✅ Factory reset complete. Reloading...', 'success'); setTimeout(() => location.reload(), 1000); }; this.commands['reset-vfs'] = (args) => { if (!args[0] || args[0] !== '--confirm') { this.terminal.print('⚠️ RESET VFS will clear all files and directories:', 'warning'); this.terminal.print(' • Virtual filesystem (all files + directories)', 'warning'); this.terminal.print(' • VFS will be reset to empty root', 'warning'); this.terminal.print(' • All settings, modules, AI profile preserved', 'info'); this.terminal.print('Type: reset-vfs --confirm to proceed', 'error'); return; } this.terminal.fs.root = { type: 'dir', content: {} }; this.terminal.fs.currentPath = []; this.terminal.fs.saveToStorage(); this.terminal.print('✅ VFS reset complete. Reloading...', 'success'); setTimeout(() => location.reload(), 1000); }; this.commands['logs'] = (args) => { const n = parseInt(args[0]) || 20; const level = args[1] ? args[1].toUpperCase() : null; const content = this.terminal.fs.readFile('/logs/current.log'); if (!content) { this.terminal.print('No logs yet', 'info'); return; } let lines = content.split('\n').filter(l => l); if (level) lines = lines.filter(l => l.includes('[' + level + ']')); const tail = lines.slice(-n); if (tail.length === 0) { this.terminal.print('No matching log entries', 'info'); return; } this.terminal.print('--- Logs (last ' + tail.length + ') ---', 'info'); tail.forEach(l => this.terminal.print(l, l.includes('[ERROR]') ? 'error' : 'info')); }; // Auto-rotate logs on startup (silently) this._autoRotateLogs(); this.commands['logrotate'] = () => { const rotated = this._autoRotateLogs(true); if (rotated) { this.terminal.print('Log rotated: ' + rotated, 'success'); } else { this.terminal.print('Log rotation not needed (<50KB, <500 lines)', 'info'); } }; this.commands['health-check'] = () => this._healthCheck(); this.commands['memory-map-export'] = () => this._memoryMapExport(); this.commands['font-size'] = (args) => { if (!args.length || !args[0]) { const cur = localStorage.getItem('webx11_font_size'); this.terminal.print(`Current font-size: ${cur || '16'}px`, 'info'); this.terminal.print('Usage: font-size <8-32>', 'info'); return; } const val = parseInt(args[0], 10); if (isNaN(val) || val < 8 || val > 32) { this.terminal.print('font-size must be between 8 and 32', 'error'); return; } localStorage.setItem('webx11_font_size', val.toString()); // Apply directly to body — bypasses CSS inheritance issues document.body.style.fontSize = val + 'px'; document.getElementById('terminal-container').style.fontSize = val + 'px'; document.getElementById('command-input').style.fontSize = val + 'px'; this.terminal.print(`Font size set to ${val}px (saved) 👆`, 'success'); }; this.commands['setfont'] = this.commands['font-size']; // Checkpoint commands (delegated to virtual-fs.js) this.commands['checkpoint'] = (args) => { try { const name = args[0] || ''; this.terminal.fs.checkpoint(name); const displayName = name || new Date().toISOString(); this.terminal.print('✅ Checkpoint created: ' + displayName, 'success'); } catch (e) { this.terminal.print('Checkpoint failed: ' + e.message, 'error'); } }; this.commands['checkpoint-list'] = () => { try { const list = this.terminal.fs.listCheckpoints(); if (!list || list.length === 0) { this.terminal.print('No checkpoints found', 'info'); return; } this.terminal.print('📋 Checkpoints:', 'text-muted'); list.forEach(c => this.terminal.print(' • ' + c)); } catch (e) { this.terminal.print('checkpoint-list failed: ' + e.message, 'error'); } }; this.commands['checkpoint-del'] = (args) => { if (!args[0]) return this.terminal.print('Usage: checkpoint-del ', 'warning'); try { if (this.terminal.fs.deleteCheckpoint(args[0])) { this.terminal.print('Deleted checkpoint: ' + args[0], 'success'); } else { this.terminal.print('Checkpoint not found: ' + args[0], 'error'); } } catch (e) { this.terminal.print('checkpoint-del failed: ' + e.message, 'error'); } }; this.commands['rollback'] = (args) => { if (!args[0]) return this.terminal.print('Usage: rollback ', 'warning'); try { if (this.terminal.fs.rollback(args[0])) { this.terminal.print('↻ Rolled back to checkpoint: ' + args[0], 'success'); } else { this.terminal.print('Rollback failed: checkpoint not found or invalid', 'error'); } } catch (e) { this.terminal.print('Rollback failed: ' + e.message, 'error'); } }; } _autoRotateLogs(force = false) { try { const content = this.terminal.fs.readFile('/logs/current.log') || ''; const lines = content.split('\n').filter(l => l); const size = content.length; if (!force && size < 51200 && lines.length < 500) return null; const now = new Date(); const stamp = now.getFullYear() + '-' + String(now.getMonth()+1).padStart(2,'0') + '-' + String(now.getDate()).padStart(2,'0') + '_' + String(now.getHours()).padStart(2,'0') + '00'; this.terminal.fs.writeFile('/logs/' + stamp + '.log', content); this.terminal.fs.writeFile('/logs/current.log', ''); // Clean oldest rotated logs (>10) const logsDir = this.terminal.fs.readDir('/logs'); if (logsDir && logsDir.content) { const rotated = Object.keys(logsDir.content) .filter(k => k.endsWith('.log') && k !== 'current.log') .sort(); while (rotated.length > 10) { const oldest = rotated.shift(); this.terminal.fs.deleteFile('/logs/' + oldest); } } return stamp + '.log'; } catch (e) { return null; } } _healthCheck() { const fs = this.terminal.fs; let warnings = 0; this.terminal.print('╔══════════════════════════════════════╗', 'info'); this.terminal.print('║ 🔍 SYSTEM HEALTH CHECK ║', 'info'); this.terminal.print('╚══════════════════════════════════════╝', 'info'); // VFS size const dump = JSON.stringify(fs.root).length; const vfsOk = dump < 524288; this.terminal.print(`VFS size: ${(dump/1024).toFixed(1)}KB ${vfsOk ? '✅' : '⚠️ > 512KB'}`, vfsOk ? 'info' : 'warning'); if (!vfsOk) warnings++; // Modules const modTotal = Object.keys(this.terminal.moduleManager.modules || {}).length; this.terminal.print(`Modules loaded: ${modTotal} ✅`, 'info'); // Logs const logContent = fs.readFile('/logs/current.log') || ''; const logLines = logContent.split('\n').filter(l => l).length; this.terminal.print(`Current log: ${logLines} lines`, logLines > 500 ? 'warning' : 'info'); if (logLines > 500) warnings++; // Trash const trashList = fs.listTrash ? fs.listTrash() : []; this.terminal.print(`Trash items: ${trashList.length}`, 'info'); // Skills const skillIdx = fs.fsReadJSON ? (fs.fsReadJSON('/skills/index.json') || {}) : {}; const skillCount = Object.keys(skillIdx).length; this.terminal.print(`Skills: ${skillCount}`, skillCount > 0 ? 'info' : 'warning'); if (skillCount === 0) warnings++; // Summary this.terminal.print('─'.repeat(40), 'text-muted'); if (warnings === 0) { this.terminal.print('All checks passed ✅', 'success'); } else { this.terminal.print(`${warnings} warning(s) found`, 'warning'); } } _memoryMapExport() { const fs = this.terminal.fs; const idx = fs.fsReadJSON ? (fs.fsReadJSON('/memory/index.json') || {}) : null; if (!idx) { this.terminal.print('No memory index found at /memory/index.json', 'error'); return; } this.terminal.print('---\nname: adaptive-memory-map\ndescription: Auto-generated from memory/index.json\ntags: [memory, context, tracking, adaptation]\ncategory: system\n---\n', 'info'); for (const [k,v] of Object.entries(idx)) { this.terminal.print(`## ${k}\n- Tasks: ${v.tasks || 0}\n- Updated: ${v.updated || 'unknown'}\n- Summary: ${(v.summary || '').substring(0,100)}...`, 'info'); } this.terminal.print('(Not saved — render to file with memory-map-export > /memory/reference.md if desired)', 'text-muted'); } handleHelp(args) { const helpMap = this.terminal.moduleManager.getAllHelp(); // Single command lookup if (args && args.length > 0) { const cmd = args[0].toLowerCase(); const allCommands = this.terminal.moduleManager.getAllCommands(); if (allCommands[cmd]) { const helpEntry = helpMap[cmd]; if (helpEntry) { this.terminal.print(helpEntry, 'info'); } else { this.terminal.print(`No help available for ${cmd}`, 'warning'); } } else { this.terminal.print(`No help available for ${cmd}`, 'warning'); } return; } // Dynamically group commands by owning module const cmdToModule = this.terminal.moduleManager.getAllCommands(); const moduleGroups = {}; for (const [cmd, modName] of Object.entries(cmdToModule)) { if (!moduleGroups[modName]) moduleGroups[modName] = []; moduleGroups[modName].push(cmd); } const modLabels = { FileSystemModule: '📁 Filesystem', WebX11AIAssistantModule: '🤖 AI', WebXAIAliasModule: '🌐 WebX', DuckDuckGoModule: '🔍 Search & Web', SearXNGModule: '🔍 Search & Web', WebFetcherModule: '🔍 Search & Web', AliasModule: '🔧 Tools', BusyBoxModule: '🔧 Tools', CalcModule: '🔧 Tools', CronModule: '🔧 Tools', CryptoGzipModule: '🔧 Tools', DiffModule: '🔧 Tools', EditorModule: '🔧 Tools', FileManagerModule: '🔧 Tools', FinderModule: '🔧 Tools', MindmapModule: '🔧 Tools', ThemeModule: '🔧 Tools', TutorialModule: '🔧 Tools', VoiceModule: '🔧 Tools', WpmModule: '🔧 Tools', SystemModule: '⚙️ System', ShellModule: '⚙️ System', ServerMonitorModule: '📡 Network', ModuleManagerModule: '🧩 Modules', OllamaModule: '🦙 Ollama', 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', }; // Merge modules with the same label const grouped = {}; const usedLabels = new Set(); for (const [modName, cmds] of Object.entries(moduleGroups)) { const label = modLabels[modName] || `🧩 ${modName.replace(/Module$/, '')}`; if (!grouped[label]) grouped[label] = []; grouped[label].push(...cmds); usedLabels.add(label); } let result = '╔══════════════════════════════════════╗\n║ 🔷 WebX11 ' + (window.NEWOS4_VERSION || 'v4.0') + ' — All Commands ║\n╚══════════════════════════════════════╝\n\n'; const total = Object.keys(helpMap).length; result += `📊 ${total} commands loaded\n\n`; for (const [label, cmds] of Object.entries(grouped)) { result += label + '\n'; cmds.forEach(c => { const desc = (helpMap[c] || c).split(' — ')[1] || ''; const short = desc.substring(0, 55); result += ` ${c.padEnd(16)}${short}\n`; }); result += '\n'; } result += '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n help → detailed usage\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'; this.terminal.print(result.trim(), 'info'); } }