// 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`; }; this.commands['status'] = () => { this.terminal.print(`System: WebX11 v4.2`); 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(); } _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; } // Categorize all commands const cats = { files: ['ls','ll','cat','put','rm','mv','cp','mkdir','rmdir','cd','pwd','find','grep','head','tail','sort','wc','touch','tree','du','upload','download','vfs-export','vfs-import','edit','diff'], ai: ['ai','ask','ai-clear','ai-system','ai-context','ai-allow','ai-deny','ai-ignore','ai-stop','ai-model'], search: ['search','s','ddg','duck','fetch','web','extract'], tools: ['calc','cron-every','cron-list','cron-stop','cron-clear','wpm','mindmap','theme','voice','alias','unalias', 'which','env','date','cal','yes','seq','base64','printf','cut','uniq','hostname','whoami','basename','dirname','sleep','expand','tutorial'], system: ['help','clear','cls','echo','mem','status','font-size','setfont','smart-prompt','logs','preview','factory-reset','reset-vfs','export','unset','script','set','test'], net: ['server','servers','server-check','server-remove','monitor'], mods: ['modules','listmods','loadmod','removemod','load-local','loadlocal','startup-modules'], ollama: ['ollama','ollamaconnect','ollamadisconnect','ollamamodels','ollamasetmodel','ollamasetproxy','models','model-info','model-pull','model-delete','model-run'], skills: ['skill-list','skill-view','skill-create','skill-patch','skill-delete','skill-export','skill-import','skill-sync','skill-pack-core'], config: ['config','config-set','config-list','config-delete','config-export','config-reset'], webx: ['webxai','wx','webx','context','contexts','context-new','context-delete','smart-ask','prompt','prompt-save','prompt-list','prompt-delete','prompt-var','prompt-vars'], data: ['my','my-set','my-get','my-list','my-export','my-import','my-clear','export-mods','import-mods','backup','restore','share-mod'], ext: ['chat','chat-setapi','chat-setmodel','chat-ask','webdav-config','webdav-status','webdav-ls','webdav-push','webdav-pull','webdav-backup','webdav-restore','webdav-backup-list'], gui: ['gui','gui-button','gui-input','gui-text','gui-clear','gui-run'], }; const labels = { files:'📁 Filesystem', ai:'🤖 AI', search:'🔍 Search & Web', tools:'🔧 Tools', system:'⚙️ System', net:'📡 Network', mods:'🧩 Modules', ollama:'🦙 Ollama', skills:'📚 Skills', config:'⚙️ Config', webx:'🌐 WebX', data:'💾 Data', ext:'🧰 Extras', gui:'🖥️ GUI' }; let result = '╔══════════════════════════════════════╗\n║ 🔷 WebX11 v4.2 — All Commands ║\n╚══════════════════════════════════════╝\n\n'; const total = Object.keys(helpMap).length; result += `📊 ${total} commands loaded\n\n`; for (const [key, cmds] of Object.entries(cats)) { const present = cmds.filter(c => helpMap[c]); if (present.length === 0) continue; result += labels[key] + '\n'; present.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'); } }