diff options
| author | Agando <roduccomm@gmail.com> | 2026-06-11 18:00:19 +0200 |
|---|---|---|
| committer | Agando <roduccomm@gmail.com> | 2026-06-11 18:00:19 +0200 |
| commit | 76e26bb6f16e35bffbf76896957bb9753241833c (patch) | |
| tree | 25bd27029c2cad1d46208bb3ae026e8272e0a6bf /modules/core/module-system.js | |
| parent | c3d5fe64c268d7ca85d1e303b98201391f28c9e9 (diff) | |
auto: 2026-06-11_18:00 changes
Diffstat (limited to 'modules/core/module-system.js')
| -rw-r--r-- | modules/core/module-system.js | 292 |
1 files changed, 281 insertions, 11 deletions
diff --git a/modules/core/module-system.js b/modules/core/module-system.js index cfb7cc4..28a19f3 100644 --- a/modules/core/module-system.js +++ b/modules/core/module-system.js @@ -8,15 +8,20 @@ return { 'help': 'help [command] — Show all commands or usage for a specific command', 'echo': 'echo <text...> — Print text to the terminal', - 'mem': 'mem — Show current memory usage', + 'mem': 'mem — Show memory usage (heap + localStorage + VFS)', 'status': 'status — Display system status (network, files, modules, Ollama)', + 'version': 'version — Show version, build date, and cache key', + 'uptime': 'uptime — Show time since page load + last save time', + 'errors': 'errors [n] — Show last n ERROR log entries (default: 20)', + '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 <file.html> — 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', + 'memory-map-export': 'memory-map-export — Export memory index to /memory/reference.md (overwrites file)', '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)', @@ -24,17 +29,46 @@ 'checkpoint-list': 'checkpoint-list — List all VFS checkpoints', 'rollback': 'rollback <name> — Restore VFS from a checkpoint', 'checkpoint-del': 'checkpoint-del <name> — 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`; + const heap = performance.memory ? (performance.memory.usedJSHeapSize / 1048576).toFixed(2) : 'N/A'; + const heapLimit = performance.memory ? (performance.memory.jsHeapSizeLimit / 1048576).toFixed(0) : 'N/A'; + // localStorage size + let lsBytes = 0; + try { + for (let i = 0; i < localStorage.length; i++) { + const k = localStorage.key(i); + if (k) lsBytes += (k.length + (localStorage.getItem(k) || '').length) * 2; + } + } catch (e) {} + const lsKB = (lsBytes / 1024).toFixed(1); + // VFS size (in-memory) + let vfsBytes = 0; + try { vfsBytes = JSON.stringify(this.terminal.fs.root).length; } catch (e) {} + const vfsKB = (vfsBytes / 1024).toFixed(1); + // sessionStorage size + let ssBytes = 0; + try { + for (let i = 0; i < sessionStorage.length; i++) { + const k = sessionStorage.key(i); + if (k) ssBytes += (k.length + (sessionStorage.getItem(k) || '').length) * 2; + } + } catch (e) {} + const ssKB = (ssBytes / 1024).toFixed(1); + // Total + const total = (parseFloat(heap === 'N/A' ? '0' : heap) + parseFloat(lsKB) + parseFloat(ssKB) + parseFloat(vfsKB)).toFixed(1); + + this.terminal.print('💾 Memory Usage', 'header'); + this.terminal.print(` JS heap: ${heap} MB${heap !== 'N/A' ? ' / ' + heapLimit + ' MB limit' : ''}`, parseFloat(heap) > 100 ? 'warning' : ''); + this.terminal.print(` localStorage: ${lsKB} KB (${localStorage.length} keys)`, parseFloat(lsKB) > 1024 ? 'warning' : ''); + this.terminal.print(` sessionStorage: ${ssKB} KB (${sessionStorage.length} keys)`, ''); + this.terminal.print(` VFS in-memory: ${vfsKB} KB`, parseFloat(vfsKB) > 256 ? 'warning' : ''); + this.terminal.print(` Total: ${total} MB`, ''); + document.getElementById('header-status').innerText = `Mem: ${heap}MB`; }; // Refresh memory display on boot setTimeout(() => { @@ -50,6 +84,224 @@ this.terminal.print(`Modules: ${Object.keys(this.terminal.moduleManager.modules).length} loaded`); this.terminal.print(`Ollama: ${this.terminal.ollamaManager.connected ? 'Connected' : 'Disconnected'}`); }; + + // v4.5.43: version — show version, build date, cache key + this.commands['version'] = () => { + const v = window.NEWOS4_VERSION || 'unknown'; + const cacheKey = localStorage.getItem('webx11_cache_ver') || 'unknown'; + // Try to get build date from script tag or module list + const buildDate = (() => { + try { + const scripts = document.querySelectorAll('script[src*="v="]'); + for (const s of scripts) { + const m = s.src.match(/v=([0-9-]+-v[0-9.]+[a-z]?)/); + if (m) return m[1].split('-').slice(0, 3).join('-'); + } + } catch (e) {} + return 'unknown'; + })(); + const ua = navigator.userAgent.split(' ').slice(-1)[0] || 'unknown'; + this.terminal.print('ℹ️ WebX11 newOS4', 'header'); + this.terminal.print(` Version: ${v}`); + this.terminal.print(` Build: ${buildDate}`); + this.terminal.print(` Cache key: ${cacheKey}`); + this.terminal.print(` Browser: ${ua}`); + this.terminal.print(` Online: ${navigator.onLine ? 'yes' : 'no'}`); + }; + + // v4.5.43: uptime — time since page load + last save time + this.commands['uptime'] = () => { + const loadTime = performance.timeOrigin || Date.now() - (window.__pageLoadMs || 0); + const ms = Date.now() - loadTime; + const s = Math.floor(ms / 1000); + const days = Math.floor(s / 86400); + const hours = Math.floor((s % 86400) / 3600); + const mins = Math.floor((s % 3600) / 60); + const secs = s % 60; + let uptime; + if (days > 0) uptime = `${days}d ${hours}h ${mins}m`; + else if (hours > 0) uptime = `${hours}h ${mins}m ${secs}s`; + else if (mins > 0) uptime = `${mins}m ${secs}s`; + else uptime = `${secs}s`; + this.terminal.print('⏱️ Uptime', 'header'); + this.terminal.print(` Since load: ${uptime} (${s}s)`); + // Check last save time from localStorage modification + try { + const lastSave = localStorage.getItem('webx11_last_save'); + if (lastSave) { + const ago = Math.floor((Date.now() - parseInt(lastSave)) / 1000); + this.terminal.print(` Last save: ${ago}s ago`, ago > 300 ? 'warning' : ''); + } else { + this.terminal.print(` Last save: unknown`, 'info'); + } + } catch (e) {} + }; + + // v4.5.43: errors [n] — alias for logs [n] ERROR + this.commands['errors'] = (args) => { + const n = parseInt(args[0]) || 20; + const content = this.terminal.fs.readFile('/logs/current.log'); + if (!content) { this.terminal.print('No logs yet', 'info'); return; } + const lines = content.split('\n').filter(l => l && l.includes('[ERROR]')); + const tail = lines.slice(-n); + if (tail.length === 0) { this.terminal.print('✅ No errors found in logs', 'success'); return; } + this.terminal.print(`--- Errors (last ${tail.length}) ---`, 'error'); + tail.forEach(l => this.terminal.print(l, 'error')); + }; + + // v4.5.43: doctor — run all health checks, rank issues, show top 3 + this.commands['doctor'] = () => { + const issues = []; + const fs = this.terminal.fs; + + // Check 1: Network + if (!navigator.onLine) issues.push({ severity: 'high', msg: 'Network: offline' }); + + // Check 2: Ollama + if (!this.terminal.ollamaManager.connected) { + issues.push({ severity: 'medium', msg: 'Ollama: not connected (AI features limited)' }); + } + + // Check 3: VFS size + try { + const vfsBytes = JSON.stringify(fs.root).length; + if (vfsBytes > 524288) issues.push({ severity: 'high', msg: `VFS: ${(vfsBytes/1024).toFixed(0)}KB > 512KB limit` }); + else if (vfsBytes > 262144) issues.push({ severity: 'medium', msg: `VFS: ${(vfsBytes/1024).toFixed(0)}KB > 256KB (consider reset-vfs)` }); + } catch (e) {} + + // Check 4: Heap + if (performance.memory) { + const heapMB = performance.memory.usedJSHeapSize / 1048576; + if (heapMB > 200) issues.push({ severity: 'high', msg: `Heap: ${heapMB.toFixed(0)}MB > 200MB` }); + else if (heapMB > 100) issues.push({ severity: 'medium', msg: `Heap: ${heapMB.toFixed(0)}MB > 100MB` }); + } + + // Check 5: Logs + try { + const logContent = fs.readFile('/logs/current.log') || ''; + const logLines = logContent.split('\n').filter(l => l).length; + if (logLines > 500) issues.push({ severity: 'low', msg: `Logs: ${logLines} lines (consider logrotate)` }); + } catch (e) {} + + // Check 6: Errors in last 100 log lines + try { + const logContent = fs.readFile('/logs/current.log') || ''; + const recent = logContent.split('\n').filter(l => l).slice(-100); + const errorCount = recent.filter(l => l.includes('[ERROR]')).length; + if (errorCount > 5) issues.push({ severity: 'high', msg: `Errors: ${errorCount} errors in last 100 log lines` }); + else if (errorCount > 0) issues.push({ severity: 'medium', msg: `Errors: ${errorCount} errors in last 100 log lines` }); + } catch (e) {} + + // Check 7: localStorage quota + let lsBytes = 0; + try { + for (let i = 0; i < localStorage.length; i++) { + const k = localStorage.key(i); + if (k) lsBytes += (k.length + (localStorage.getItem(k) || '').length) * 2; + } + } catch (e) {} + if (lsBytes > 4 * 1024 * 1024) issues.push({ severity: 'high', msg: `localStorage: ${(lsBytes/1024/1024).toFixed(1)}MB approaching 5MB quota` }); + + // Check 8: Disabled modules + let disabled = []; + try { disabled = JSON.parse(localStorage.getItem('webx11_module_disabled') || '[]'); } catch (e) {} + if (disabled.length > 0) { + issues.push({ severity: 'low', msg: `Disabled modules: ${disabled.length} (${disabled.join(', ')})` }); + } + + this.terminal.print('🩺 Doctor Report', 'header'); + if (issues.length === 0) { + this.terminal.print(' ✅ All systems healthy', 'success'); + return; + } + // Sort by severity: high > medium > low + const sevOrder = { high: 0, medium: 1, low: 2 }; + issues.sort((a, b) => sevOrder[a.severity] - sevOrder[b.severity]); + this.terminal.print(` Found ${issues.length} issue(s):`, 'warning'); + for (const i of issues.slice(0, 5)) { + const icon = i.severity === 'high' ? '🔴' : i.severity === 'medium' ? '🟡' : '🟢'; + this.terminal.print(` ${icon} [${i.severity.toUpperCase()}] ${i.msg}`, i.severity === 'high' ? 'error' : 'warning'); + } + if (issues.length > 5) this.terminal.print(` ... and ${issues.length - 5} more`, 'info'); + this.terminal.print('', ''); + this.terminal.print(' Recommended actions:', 'info'); + if (issues.some(i => i.msg.startsWith('Network'))) this.terminal.print(' → Check internet connection'); + if (issues.some(i => i.msg.startsWith('Ollama'))) this.terminal.print(' → Check Ollama is running (ollama serve)'); + if (issues.some(i => i.msg.startsWith('VFS'))) this.terminal.print(' → Run reset-vfs --confirm to clear or checkpoint/rollback'); + if (issues.some(i => i.msg.startsWith('Heap'))) this.terminal.print(' → Refresh the page to clear memory'); + if (issues.some(i => i.msg.startsWith('Logs'))) this.terminal.print(' → Run logrotate'); + if (issues.some(i => i.msg.startsWith('Errors'))) this.terminal.print(' → Run errors to see details, or bug-report to save state'); + if (issues.some(i => i.msg.startsWith('localStorage'))) this.terminal.print(' → Run factory-reset --confirm (last resort)'); + }; + + // v4.5.43: bug-report — generate markdown report to /tmp/ + this.commands['bug-report'] = () => { + const fs = this.terminal.fs; + const lines = []; + const ts = new Date().toISOString(); + lines.push(`# WebX11 newOS4 Bug Report`); + lines.push(`Generated: ${ts}`); + lines.push(''); + lines.push('## System'); + lines.push(`- Version: ${window.NEWOS4_VERSION || 'unknown'}`); + lines.push(`- Cache key: ${localStorage.getItem('webx11_cache_ver') || 'unknown'}`); + lines.push(`- Online: ${navigator.onLine ? 'yes' : 'no'}`); + lines.push(`- Browser: ${navigator.userAgent}`); + lines.push(`- Screen: ${screen.width}x${screen.height}`); + lines.push(`- Heap: ${performance.memory ? (performance.memory.usedJSHeapSize / 1048576).toFixed(1) + 'MB' : 'N/A'}`); + lines.push(`- VFS size: ${(JSON.stringify(fs.root).length / 1024).toFixed(1)}KB`); + lines.push(`- Modules loaded: ${Object.keys(this.terminal.moduleManager.modules).length}`); + lines.push(`- Ollama: ${this.terminal.ollamaManager.connected ? 'connected' : 'disconnected'}`); + lines.push(''); + // Uptime + const loadTime = performance.timeOrigin || Date.now(); + const upSec = Math.floor((Date.now() - loadTime) / 1000); + lines.push(`- Uptime: ${upSec}s`); + lines.push(''); + lines.push('## Recent Errors (last 20)'); + try { + const logContent = fs.readFile('/logs/current.log') || ''; + const errs = logContent.split('\n').filter(l => l && l.includes('[ERROR]')).slice(-20); + if (errs.length === 0) lines.push('_No errors found_'); + else errs.forEach(e => lines.push('- ' + e)); + } catch (e) { lines.push('_Could not read logs_'); } + lines.push(''); + lines.push('## Recent Commands (last 10)'); + try { + const hist = JSON.parse(localStorage.getItem('webx11_command_history') || '[]'); + const recent = hist.slice(-10); + if (recent.length === 0) lines.push('_No history_'); + else recent.forEach(c => lines.push('- ' + c)); + } catch (e) { lines.push('_No history available_'); } + lines.push(''); + lines.push('## Disabled Modules'); + try { + const disabled = JSON.parse(localStorage.getItem('webx11_module_disabled') || '[]'); + if (disabled.length === 0) lines.push('_None_'); + else disabled.forEach(d => lines.push('- ' + d)); + } catch (e) { lines.push('_Unknown_'); } + lines.push(''); + lines.push('## File List (top-level VFS)'); + try { + const items = fs.ls(); + items.slice(0, 20).forEach(i => lines.push('- ' + i)); + if (items.length > 20) lines.push(`- ... and ${items.length - 20} more`); + } catch (e) { lines.push('_Could not list_'); } + lines.push(''); + lines.push('---'); + lines.push('Send this file when reporting a bug.'); + + const content = lines.join('\n'); + const filename = `/tmp/bug-report-${Date.now()}.md`; + try { + fs.writeFile(filename, content); + this.terminal.print(`✅ Bug report saved to ${filename}`, 'success'); + this.terminal.print(` (${(content.length / 1024).toFixed(1)}KB)`, 'info'); + } catch (e) { + this.terminal.print(`❌ Could not save bug report: ${e.message}`, 'error'); + } + }; + this.commands['preview'] = (args) => this.terminal.openPreview(args); this.commands['factory-reset'] = (args) => { if (!args[0] || args[0] !== '--confirm') { @@ -253,11 +505,29 @@ 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'); + // v4.5.43: Actually export to /memory/reference.md (was: print to terminal) + const lines = []; + lines.push('---'); + lines.push('name: adaptive-memory-map'); + lines.push('description: Auto-generated from memory/index.json'); + lines.push('tags: [memory, context, tracking, adaptation]'); + lines.push('category: system'); + lines.push('---'); + lines.push(''); + for (const [k, v] of Object.entries(idx)) { + lines.push(`## ${k}`); + lines.push(`- Tasks: ${v.tasks || 0}`); + lines.push(`- Updated: ${v.updated || 'unknown'}`); + lines.push(`- Summary: ${(v.summary || '').substring(0, 200)}...`); + lines.push(''); + } + const content = lines.join('\n'); + try { + fs.writeFile('/memory/reference.md', content); + this.terminal.print(`✅ Memory map exported to /memory/reference.md (${(content.length / 1024).toFixed(1)}KB)`, 'success'); + } catch (e) { + this.terminal.print(`❌ Could not save: ${e.message}`, 'error'); } - 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(); |