diff options
31 files changed, 7391 insertions, 927 deletions
diff --git a/api/course/event.php b/api/course/event.php new file mode 100644 index 0000000..696d1c5 --- /dev/null +++ b/api/course/event.php @@ -0,0 +1,40 @@ +<?php +// api/course/event.php — anonymous course telemetry. No IP/UA stored. +header('Content-Type: application/json; charset=utf-8'); +header('Access-Control-Allow-Origin: https://webx11.duckdns.org'); +header('Access-Control-Allow-Methods: POST, OPTIONS'); +header('Access-Control-Allow-Headers: Content-Type'); +if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(204); exit; } +if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); echo json_encode(['error' => 'POST only']); exit; } + +$raw = file_get_contents('php://input'); +if ($raw === false || strlen($raw) === 0) { http_response_code(400); echo json_encode(['error' => 'empty body']); exit; } +if (strlen($raw) > 4096) { http_response_code(413); echo json_encode(['error' => 'payload too large']); exit; } +$p = json_decode($raw, true); +if (!is_array($p)) { http_response_code(400); echo json_encode(['error' => 'invalid json']); exit; } + +$sid = isset($p['sid']) ? substr(preg_replace('/[^a-zA-Z0-9_-]/', '', (string)$p['sid']), 0, 64) : ''; +$type = isset($p['type']) ? (string)$p['type'] : ''; +$allowed = ['lesson_open','lesson_complete','exercise_fail','question','project_open','project_view','quiz_answer']; +if (strlen($sid) < 8 || !in_array($type, $allowed, true)) { http_response_code(400); echo json_encode(['error' => 'sid/type invalid']); exit; } + +$ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'; +$dir = __DIR__ . '/../../data/course'; +if (!is_dir($dir)) { mkdir($dir, 0775, true); } +$rlFile = $dir . '/.rl-' . substr(hash('sha256', $ip), 0, 16) . '-' . date('YmdH'); +$hits = file_exists($rlFile) ? (int)file_get_contents($rlFile) : 0; +if ($hits > 120) { http_response_code(429); echo json_encode(['error' => 'rate limited']); exit; } +file_put_contents($rlFile, (string)($hits + 1), LOCK_EX); +if (random_int(1, 50) === 1) { + foreach (glob($dir . '/.rl-*') as $f) { if (filemtime($f) < time() - 172800) @unlink($f); } +} + +$detail = isset($p['detail']) && is_array($p['detail']) ? array_slice($p['detail'], 0, 8, true) : []; +foreach ($detail as $k => $v) { if (is_string($v)) $detail[$k] = substr($v, 0, 300); } +$row = ['ts' => gmdate('c'), 'type' => $type, 'sid' => $sid, + 'version' => substr((string)($p['version'] ?? ''), 0, 40), 'detail' => $detail]; +$out = $dir . '/events-' . date('Ym') . '.jsonl'; +$fp = fopen($out, 'a'); +if ($fp && flock($fp, LOCK_EX)) { fwrite($fp, json_encode($row, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n"); flock($fp, LOCK_UN); fclose($fp); } +http_response_code(200); +echo json_encode(['ok' => true]); diff --git a/course.html b/course.html new file mode 100644 index 0000000..c61a901 --- /dev/null +++ b/course.html @@ -0,0 +1,252 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="theme-color" content="#0a0a14"> +<title>newOS4 — Interactive Course</title> +<style> +:root{--bg:#0a0a14;--bg2:#141428;--bg3:#1c1c36;--text:#e8e8ff;--text2:#b0b0cc;--accent:#818cf8;--accent2:#a78bfa; +--green:#34d399;--cyan:#22d3ee;--amber:#fbbf24;--rose:#fb7185;--border:#3a3a5a;--radius:12px; +--mono:'JetBrains Mono','Fira Code',monospace;--sans:-apple-system,BlinkMacSystemFont,'Segoe UI',Inter,Roboto,sans-serif} +*{box-sizing:border-box;margin:0;padding:0} +body{background:radial-gradient(1200px 600px at 20% -10%,#1b1b3a 0,var(--bg) 55%);color:var(--text);font-family:var(--sans);min-height:100vh} +a{color:var(--accent)} code,pre{font-family:var(--mono)} +header{position:sticky;top:0;z-index:20;display:flex;gap:10px;align-items:center;padding:12px 20px;background:rgba(10,10,20,.86);backdrop-filter:blur(14px);border-bottom:1px solid var(--border);flex-wrap:wrap} +header h1{font-size:1.05rem;letter-spacing:.3px} +.chip{font-size:.72rem;padding:3px 9px;border:1px solid var(--border);border-radius:999px;color:var(--text2)} +.btn{background:var(--bg3);border:1px solid var(--border);color:var(--text);border-radius:9px;padding:7px 12px;cursor:pointer;font-size:.82rem;text-decoration:none;display:inline-block} +.btn:hover{border-color:var(--accent);color:#fff} +.btn.primary{background:linear-gradient(135deg,var(--accent),var(--accent2));border:0;color:#0b0b18;font-weight:700} +.grow{flex:1} +.layout{display:grid;grid-template-columns:280px minmax(0,1fr) 300px;gap:18px;padding:18px;max-width:1500px;margin:0 auto} +@media(max-width:1050px){.layout{grid-template-columns:1fr}.rail{display:none}} +.panel{background:rgba(20,20,40,.72);border:1px solid var(--border);border-radius:var(--radius);padding:14px} +.side h3,.rail h3{font-size:.75rem;text-transform:uppercase;letter-spacing:.12em;color:var(--text2);margin-bottom:10px} +.chapter{display:flex;justify-content:space-between;gap:8px;padding:8px 10px;border-radius:9px;cursor:pointer;font-size:.85rem} +.chapter:hover,.chapter.active{background:var(--bg3)} +.chapter small{color:var(--text2)} +.lesson{padding:6px 10px 6px 22px;font-size:.8rem;color:var(--text2);cursor:pointer;border-left:2px solid transparent} +.lesson:hover{color:var(--text)} .lesson.done{color:var(--green);border-left-color:var(--green)} +.bar{height:8px;background:var(--bg3);border-radius:99px;overflow:hidden} +.bar>i{display:block;height:100%;width:0;background:linear-gradient(90deg,var(--accent),var(--cyan));transition:width .3s} +main h2{font-size:1.35rem;margin-bottom:6px} +.meta{color:var(--text2);font-size:.82rem;margin-bottom:16px} +.block{margin:12px 0;line-height:1.65} +.block.cmd{background:#0d0d1c;border:1px solid var(--border);border-left:3px solid var(--green);border-radius:8px;padding:10px 12px;font-family:var(--mono);color:#b8f5d8;display:flex;justify-content:space-between;gap:10px;align-items:center;flex-wrap:wrap} +.block.tip,.block.warn{border-radius:8px;padding:10px 12px;font-size:.9rem} +.block.tip{background:rgba(52,211,153,.08);border:1px solid rgba(52,211,153,.3)} +.block.warn{background:rgba(251,113,133,.08);border:1px solid rgba(251,113,133,.35)} +.block.table{background:rgba(34,211,238,.06);border:1px solid rgba(34,211,238,.25);border-radius:8px;padding:10px 12px;font-size:.85rem} +.exercise{margin-top:18px;background:rgba(129,140,248,.07);border:1px solid rgba(129,140,248,.35);border-radius:10px;padding:14px} +.quiz{margin-top:14px;border-top:1px dashed var(--border);padding-top:14px} +.opt{display:block;width:100%;text-align:left;margin:6px 0;padding:8px 10px;background:var(--bg3);border:1px solid var(--border);border-radius:8px;color:var(--text);cursor:pointer;font-size:.85rem} +.opt:hover{border-color:var(--accent)} +.rail .search{width:100%;padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:9px;color:var(--text);font-size:.82rem} +.atlas-item{padding:7px 0;border-bottom:1px solid rgba(58,58,90,.4);font-size:.8rem} +.atlas-item code{color:var(--cyan)} .atlas-item span{color:var(--text2);display:block;margin-top:2px} +#tutor-log{max-height:260px;overflow:auto;font-size:.8rem;color:var(--text2);margin:8px 0} +.small{font-size:.75rem;color:var(--text2)} +</style> +</head> +<body> +<header> + <h1>📚 newOS4 <span style="color:var(--accent)">Course</span></h1> + <span class="chip" id="ver">loading…</span> + <span class="chip" id="xp">0 XP</span> + <div class="grow"></div> + <button class="btn" onclick="showProjects()">Projects</button> + <button class="btn" onclick="showFeed()">Daily</button> + <a class="btn" href="main.html">⌨ Terminal</a> + <a class="btn" href="help.html">Help</a> + <a class="btn" href="modules.html">Modules</a> +</header> +<div class="layout"> + <aside class="panel side"> + <h3>Curriculum</h3> + <div class="bar" style="margin-bottom:12px"><i id="pbar"></i></div> + <div id="chapters"><p class="small">Loading…</p></div> + <div style="margin-top:14px;border-top:1px solid var(--border);padding-top:10px"> + <label class="small" style="display:flex;gap:6px;align-items:center;cursor:pointer"> + <input type="checkbox" id="telemetry" onchange="setTelemetry(this.checked)"> Share anonymous progress + </label> + <button class="btn" style="width:100%;margin-top:8px" onclick="exportProgress()">Export progress</button> + </div> + </aside> + <main class="panel" id="reader"><p class="small">Loading course…</p></main> + <aside class="panel rail"> + <h3>Command atlas</h3> + <input class="search" id="atlas-q" placeholder="filter 344 commands…" autocomplete="off"> + <div id="atlas" style="margin-top:8px;max-height:400px;overflow:auto"></div> + <h3 style="margin-top:16px">Ask the tutor</h3> + <div id="tutor-log"></div> + <input class="search" id="tutor-q" placeholder="ask about this lesson…" autocomplete="off"> + <button class="btn primary" style="width:100%;margin-top:8px" id="tutor-send">Ask AI tutor</button> + </aside> +</div> +<script> +const BASE = '/spaces/newos4/course/'; +const KEY = 'webx11_course_progress_v1'; +const V = Date.now().toString(36); +let MANIFEST = null, ATLAS = null, ACTIVE = null; +const $ = id => document.getElementById(id); +const esc = s => String(s).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); +const prog = () => { try { return JSON.parse(localStorage.getItem(KEY)) || {completed:{},xp:0}; } catch(e){ return {completed:{},xp:0}; } }; +const save = p => localStorage.setItem(KEY, JSON.stringify(p)); + +async function boot(){ + try{ + const [m,a] = await Promise.all([ + fetch(BASE+'course.json?v='+V).then(r=>r.json()), + fetch(BASE+'atlas.json?v='+V).then(r=>r.json()).catch(()=>null) + ]); + MANIFEST = m; ATLAS = a; + $('ver').textContent = 'v' + m.version; + $('telemetry').checked = localStorage.getItem('webx11_course_telemetry') !== 'off'; + renderChapters(); renderAtlas(); renderProgress(); + }catch(e){ $('reader').innerHTML = '<p style="color:var(--rose)">Course content unreachable: '+esc(e.message)+'</p>'; } +} +function renderProgress(){ + const p = prog(), total = (MANIFEST.chapters||[]).reduce((n,c)=>n+(c.lessons||[]).length,0); + $('xp').textContent = (p.xp||0) + ' XP'; + $('pbar').style.width = total ? Math.round(Object.keys(p.completed).length/total*100)+'%' : '0%'; +} +function renderAtlas(q){ + if(!ATLAS){ $('atlas').innerHTML='<p class="small" style="color:var(--rose)">atlas.json missing</p>'; return; } + const items = Object.entries(ATLAS.commands).filter(([c,e])=> !q || (c+' '+e.desc+' '+e.module+' '+e.category).toLowerCase().includes(q.toLowerCase())); + $('atlas').innerHTML = '<p class="small" style="margin-bottom:6px">'+items.length+' / '+Object.keys(ATLAS.commands).length+' commands</p>' + + items.slice(0,60).map(([c,e])=>'<div class="atlas-item"><code>'+esc(c)+'</code><span>'+esc(e.desc||e.module)+' · '+esc(e.tier)+'</span></div>').join(''); +} +async function renderChapters(){ + const p = prog(), wrap = $('chapters'); wrap.innerHTML = ''; + for(const ch of MANIFEST.chapters||[]){ + const d = document.createElement('div'); d.className='chapter'; + d.innerHTML = '<span>'+esc(ch.icon||'📘')+' '+esc(ch.title)+'</span><small>'+ (ch.lessons||[]).filter(l=>p.completed[l.id]).length +'/'+(ch.lessons||[]).length+'</small>'; + wrap.appendChild(d); + const box = document.createElement('div'); wrap.appendChild(box); + d.onclick = () => { d.classList.toggle('active'); box.style.display = box.style.display==='none'?'block':'none'; }; + for(const l of ch.lessons||[]){ + const e = document.createElement('div'); e.className='lesson'+(p.completed[l.id]?' done':''); e.textContent = l.id+' · '+l.title; + e.onclick = () => openLesson(ch.id, l.id); box.appendChild(e); + } + } + const p2 = prog(); const all = (MANIFEST.chapters||[]).flatMap(c=>(c.lessons||[]).map(l=>({c,l}))); + const first = all.find(x=>!p2.completed[x.l.id]) || all[0]; + if(first) openLesson(first.c.id, first.l.id); +} +async function openLesson(chId, lessonId){ + $('reader').innerHTML = '<p class="small">Loading lesson…</p>'; + try{ + const ch = await fetch(BASE+'chapters/'+chId+'.json?v='+V).then(r=>r.json()); + const l = (ch.lessons||[]).find(x=>x.id===lessonId); if(!l) throw new Error('lesson not found'); + ACTIVE = {ch, l}; + const p = prog(); + let html = '<h2>'+esc(l.title)+'</h2><div class="meta">'+esc(l.objective||'')+' · ⏱ '+(l.minutes||5)+' min · 📘 '+esc(ch.title)+'</div>'; + for(const b of l.blocks||[]){ + if(b.type==='cmd') html += '<div class="block cmd"><span>$ '+esc(b.text)+'</span><button class="btn" data-copy="'+esc(b.text)+'">Copy</button></div>'; + else if(b.type==='code') html += '<pre class="block cmd">'+esc(b.text)+'</pre>'; + else if(b.type==='tip') html += '<div class="block tip">💡 '+esc(b.text)+'</div>'; + else if(b.type==='warn') html += '<div class="block warn">⚠️ '+esc(b.text)+'</div>'; + else if(b.type==='table') html += '<div class="block table">'+esc(b.text)+'</div>'; + else html += '<p class="block">'+esc(b.text)+'</p>'; + } + if(l.exercise){ + html += '<div class="exercise"><strong>🏋️ Exercise</strong><p style="margin:6px 0">'+esc(l.exercise.instruction)+'</p>' + + '<button class="btn" id="hint-btn">Show hint</button> <button class="btn primary" id="done-btn">Mark done'+(p.completed[l.id]?' ✓':'')+'</button>' + + '<p id="hint" style="display:none;margin-top:8px;color:var(--amber)">💡 '+esc(l.exercise.hint||'')+'</p>' + + '<p class="small" style="margin-top:8px">Same XP as the terminal: run it in <a href="main.html">main.html</a> with <code>course open '+esc(l.id)+'</code>.</p></div>'; + } + if(l.quiz){ + html += '<div class="quiz"><strong>❓ '+esc(l.quiz.q)+'</strong>'; + (l.quiz.options||[]).forEach((o,i)=>{ html += '<button class="opt" data-opt="'+i+'">'+(i+1)+') '+esc(o)+'</button>'; }); + html += '<p id="quiz-msg" style="margin-top:6px"></p></div>'; + } + $('reader').innerHTML = html; + $('reader').querySelectorAll('[data-copy]').forEach(b=>b.onclick=()=>{ navigator.clipboard&&navigator.clipboard.writeText(b.dataset.copy); b.textContent='Copied'; }); + if($('hint-btn')) $('hint-btn').onclick=()=>{ $('hint').style.display='block'; }; + if($('done-btn')) $('done-btn').onclick=()=>{ completeLesson(l.id); }; + $('reader').querySelectorAll('[data-opt]').forEach(b=>b.onclick=()=>{ + const ok = parseInt(b.dataset.opt,10) === l.quiz.answer; + $('quiz-msg').textContent = ok ? '✅ Correct (+5 XP)' : '❌ Not quite — re-read the lesson.'; + $('quiz-msg').style.color = ok ? 'var(--green)' : 'var(--rose)'; + if(ok) completeLesson(l.id, 5); + }); + track('lesson_open', {lesson:l.id}); + }catch(e){ $('reader').innerHTML = '<p style="color:var(--rose)">Failed: '+esc(e.message)+'</p>'; } +} +function completeLesson(id, xp){ + const p = prog(); if(p.completed[id]) return; + p.completed[id]=new Date().toISOString(); p.xp=(p.xp||0)+(xp||10); save(p); renderProgress(); renderChapters(); + track('lesson_complete',{lesson:id}); +} +function track(type, detail){ + try{ + if(localStorage.getItem('webx11_course_telemetry')==='off') return; + const sid = localStorage.getItem('webx11_course_sid') || (Math.random().toString(36).slice(2)+Date.now().toString(36)); + localStorage.setItem('webx11_course_sid', sid); + fetch('/spaces/newos4/api/course/event.php',{method:'POST',headers:{'Content-Type':'application/json'},keepalive:true, + body:JSON.stringify({sid,type,detail,version:(MANIFEST&&MANIFEST.version)||''})}).catch(()=>{}); + }catch(e){} +} +function setTelemetry(on){ localStorage.setItem('webx11_course_telemetry', on?'on':'off'); } +function exportProgress(){ + const blob = new Blob([JSON.stringify(prog(), null, 2)], {type:'application/json'}); + const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = 'newos4-course-progress.json'; a.click(); +} +function tutorContext(){ + if(!ACTIVE) return 'No lesson selected.'; + const l = ACTIVE.l; + return ['LESSON: '+l.title,'OBJECTIVE: '+l.objective,'COMMANDS: '+(l.commands||[]).join(', ')] + .concat((l.blocks||[]).map(b=>b.type.toUpperCase()+': '+b.text)) + .concat((l.exercise?['EXERCISE: '+l.exercise.instruction+' | HINT: '+l.exercise.hint]:[])).join('\n').slice(0,6000); +} +async function askTutor(){ + const q = $('tutor-q').value.trim(); if(!q) return; + const log = $('tutor-log'); log.innerHTML += '<div style="margin:6px 0;color:var(--text)"><b>You:</b> '+esc(q)+'</div>'; + $('tutor-q').value=''; + const tok = localStorage.getItem('webx11_session_id') || ''; + const headers = {'Content-Type':'application/json'}; + if(tok) headers.Authorization = ['Bearer', tok].join(' '); + const sys = 'You are the newOS4 course tutor. Answer in <=120 words using only the lesson context. If not covered, say so and point to `help <cmd>` — never invent commands.\n\n'+tutorContext(); + try{ + const r = await fetch('/ai/v1/chat/completions',{method:'POST',headers, + body:JSON.stringify({model:'gemma4:31b-cloud',stream:false,messages:[{role:'system',content:sys},{role:'user',content:q}]})}); + if(!r.ok) throw new Error('HTTP '+r.status+(r.status===401?' — log in first':'')); + const d = await r.json(); + const a = (d.choices&&d.choices[0]&&d.choices[0].message&&d.choices[0].message.content)||'(empty)'; + log.innerHTML += '<div style="margin:6px 0;color:var(--cyan)"><b>Tutor:</b> '+esc(a)+'</div>'; + track('question',{q:q.slice(0,240),lesson:ACTIVE?ACTIVE.l.id:null}); + }catch(e){ log.innerHTML += '<div style="margin:6px 0;color:var(--rose)">'+esc(e.message)+'</div>'; } + log.scrollTop = log.scrollHeight; +} +async function showProjects(){ + $('reader').innerHTML = '<h2>🛠 Example projects</h2>'; + for(const id of MANIFEST.projects||[]){ + const pr = await fetch(BASE+'projects/'+id+'.json?v='+V).then(r=>r.json()).catch(()=>null); if(!pr) continue; + const div = document.createElement('div'); div.className='exercise'; div.style.marginTop='12px'; + div.innerHTML = '<strong>'+esc(pr.title)+'</strong> <span class="chip">'+esc(pr.level||'beginner')+'</span> <span class="chip">'+pr.minutes+' min</span>' + + '<p class="small" style="margin:6px 0">'+esc(pr.goal)+'</p>' + + (pr.steps||[]).map((s,i)=>'<div class="block" style="margin:4px 0">'+(i+1)+'. '+esc(s.instruction)+(s.command?' <code style="color:var(--green)">'+esc(s.command)+'</code>':'')+'</div>').join(''); + $('reader').appendChild(div); + track('project_view',{project:id}); + } +} +async function showFeed(){ + $('reader').innerHTML = '<h2>📰 Daily lessons</h2><p class="small">AI-expanded micro-lessons, newest first.</p>'; + const f = await fetch(BASE+'feed.json?v='+V).then(r=>r.json()).catch(()=>({items:[]})); + if(!(f.items||[]).length) $('reader').innerHTML += '<p class="small" style="margin-top:10px">No daily lessons yet — the generator adds one at the next 05:15 run.</p>'; + for(const it of f.items||[]){ + const d = document.createElement('div'); d.className='exercise'; d.style.marginTop='12px'; + d.innerHTML = '<strong>'+esc(it.title)+'</strong> <span class="chip">'+esc(it.date)+'</span>' + + (it.blocks||[]).map(b=> b.type==='cmd' ? '<div class="block cmd">$ '+esc(b.text)+'</div>' : '<p class="block">'+esc(b.text)+'</p>').join('') + + (it.sources&&it.sources.length ? '<p class="small">Sources: '+it.sources.map(s=>'<a href="'+esc(s)+'" target="_blank" rel="noopener">'+esc(s.replace(/^https?:\/\//,'').slice(0,40))+'</a>').join(' · ')+'</p>' : ''); + $('reader').appendChild(d); + } +} +$('atlas-q').addEventListener('input', e => renderAtlas(e.target.value.trim())); +$('tutor-send').onclick = askTutor; +$('tutor-q').addEventListener('keydown', e => { if(e.key==='Enter') askTutor(); }); +boot(); +</script> +</body> +</html> diff --git a/course/atlas.json b/course/atlas.json new file mode 100644 index 0000000..e1c6a4e --- /dev/null +++ b/course/atlas.json @@ -0,0 +1,3768 @@ +{ + "version": "2026-10-18T12:01:30Z", + "source_hash": "3295c562f4c4dd30", + "commands": { + "a2a-ack": { + "module": "module-agent-registry", + "file": "modules/core/module-agent-registry.js", + "tier": "core-bundle", + "category": "agents", + "desc": "a2a-ack <msgId> <result...> — Acknowledge a message with result", + "builtin": false, + "example": "a2a-ack" + }, + "a2a-list": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "a2a-list — Show pending A2A messages across all agents", + "builtin": false, + "example": "a2a-list" + }, + "a2a-poll": { + "module": "module-agent-registry", + "file": "modules/core/module-agent-registry.js", + "tier": "core-bundle", + "category": "agents", + "desc": "a2a-poll [agentId] — Poll for messages addressed to an agent (default: current or \"user\")", + "builtin": false, + "example": "a2a-poll" + }, + "a2a-post": { + "module": "module-agent-registry", + "file": "modules/core/module-agent-registry.js", + "tier": "core-bundle", + "category": "agents", + "desc": "a2a-post <to> <type> <capability> <payload...> — Post an A2A message", + "builtin": false, + "example": "a2a-post" + }, + "a2a-prune": { + "module": "module-agent-registry", + "file": "modules/core/module-agent-registry.js", + "tier": "core-bundle", + "category": "agents", + "desc": "a2a-prune — Clean up expired and old completed messages", + "builtin": false, + "example": "a2a-prune" + }, + "a2a-reject": { + "module": "module-agent-registry", + "file": "modules/core/module-agent-registry.js", + "tier": "core-bundle", + "category": "agents", + "desc": "a2a-reject <msgId> <error...> — Reject a message with error", + "builtin": false, + "example": "a2a-reject" + }, + "a2a-send": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "a2a-send <agent-id> <json-message> — Send an A2A message to an agent's inbox", + "builtin": false, + "example": "a2a-send" + }, + "a2a-status": { + "module": "module-agent-registry", + "file": "modules/core/module-agent-registry.js", + "tier": "core-bundle", + "category": "agents", + "desc": "a2a-status — Show scratchpad stats per agent", + "builtin": false, + "example": "a2a-status" + }, + "agent-list": { + "module": "module-agent-registry", + "file": "modules/core/module-agent-registry.js", + "tier": "core-bundle", + "category": "agents", + "desc": "module-agent-registry.js — VFS-backed agent directory with A2A contracts", + "builtin": false, + "example": "agent-list" + }, + "agent-loops": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "agent-loops [name|new|edit|delete|clone|export|reset] — Manage agent loop profiles", + "builtin": false, + "example": "agent-loops" + }, + "agent-register": { + "module": "module-agent-registry", + "file": "modules/core/module-agent-registry.js", + "tier": "core-bundle", + "category": "agents", + "desc": "module-agent-registry.js — VFS-backed agent directory with A2A contracts", + "builtin": false, + "example": "agent-register" + }, + "agent-status": { + "module": "module-agent-registry", + "file": "modules/core/module-agent-registry.js", + "tier": "core-bundle", + "category": "agents", + "desc": "module-agent-registry.js — VFS-backed agent directory with A2A contracts", + "builtin": false, + "example": "agent-status" + }, + "agent-unregister": { + "module": "module-agent-registry", + "file": "modules/core/module-agent-registry.js", + "tier": "core-bundle", + "category": "agents", + "desc": "module-agent-registry.js — VFS-backed agent directory with A2A contracts", + "builtin": false, + "example": "agent-unregister" + }, + "ai": { + "module": "module-ollama", + "file": "modules/core/module-ollama.js", + "tier": "core-bundle", + "category": "ai", + "desc": "Extracted from newOS4 index.html — WebX11AIAssistantModule (Full Agent Loop)", + "builtin": false, + "example": "ai" + }, + "ai-allow": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "ai-allow — Confirm pending AI action", + "builtin": false, + "example": "ai-allow" + }, + "ai-clear": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "ai-clear — Clear conversation context", + "builtin": false, + "example": "ai-clear" + }, + "ai-compress": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "ai-compress — command from module-aiassist", + "builtin": false, + "example": "ai-compress" + }, + "ai-config": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "ai-config [key] [value] — View or set AI loop config (max-turns, max-context, confirm-timeout(0=disabled))", + "builtin": false, + "example": "ai-config" + }, + "ai-context": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "ai-context — Show current conversation context", + "builtin": false, + "example": "ai-context" + }, + "ai-deny": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "ai-deny — Deny pending AI action", + "builtin": false, + "example": "ai-deny" + }, + "ai-ignore": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "ai-ignore — Suppress validator warnings for this session", + "builtin": false, + "example": "ai-ignore" + }, + "ai-model": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "ai-model [model] — Show or set AI model", + "builtin": false, + "example": "ai-model" + }, + "ai-stop": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "ai-stop — Cancel current AI operation", + "builtin": false, + "example": "ai-stop" + }, + "ai-system": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "ai-system <prompt> — Set system prompt", + "builtin": false, + "example": "ai-system" + }, + "alias": { + "module": "alias", + "file": "modules/ext/alias.js", + "tier": "critical-ext-bundle", + "category": "general", + "desc": "List, set, or show aliases (usage: alias [name=value])", + "builtin": false, + "example": "alias" + }, + "analyze": { + "module": "media", + "file": "modules/ext/media.js", + "tier": "ext-auto", + "category": "general", + "desc": "analyze <file> — Analyze image/audio/video. Flags: --frames N, --spectrogram, --beats, --scene", + "builtin": false, + "example": "analyze" + }, + "archive-list": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "archive-list — command from module-aiassist", + "builtin": false, + "example": "archive-list" + }, + "archive-read": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "archive-read — command from module-aiassist", + "builtin": false, + "example": "archive-read" + }, + "ask": { + "module": "module-ollama", + "file": "modules/core/module-ollama.js", + "tier": "core-bundle", + "category": "ai", + "desc": "Extracted from newOS4 index.html — WebX11AIAssistantModule (Full Agent Loop)", + "builtin": false, + "example": "ask" + }, + "base64": { + "module": "busybox", + "file": "modules/ext/busybox.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "base64 [-d] <string> — Base64 encode/decode", + "builtin": false, + "example": "base64" + }, + "basename": { + "module": "busybox", + "file": "modules/ext/busybox.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "basename <path> — Strip directory from path", + "builtin": false, + "example": "basename" + }, + "bench": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "bench [profile] [--tasks=ids] [--repeat=N] — Run benchmark suite on a profile", + "builtin": false, + "example": "bench" + }, + "bench-compare": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "bench-compare <p1> <p2> — Compare benchmark results of two profiles", + "builtin": false, + "example": "bench-compare" + }, + "bug-report": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "bug-report — Generate a markdown bug report to /tmp/ for debugging", + "builtin": false, + "example": "bug-report" + }, + "cal": { + "module": "busybox", + "file": "modules/ext/busybox.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "cal [month] [year] — Display an ASCII calendar", + "builtin": false, + "example": "cal" + }, + "calc": { + "module": "calc", + "file": "modules/ext/calc.js", + "tier": "ext-auto", + "category": "general", + "desc": "calc <expression> or calc <value> <from-unit> to <to-unit> — Evaluate expressions or convert between units", + "builtin": false, + "example": "calc" + }, + "cat": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "cat <file> — Display file contents", + "builtin": false, + "example": "cat" + }, + "cd": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "cd <path> — Change current directory; cd / to go to root", + "builtin": false, + "example": "cd" + }, + "chat": { + "module": "module-chatstudio", + "file": "modules/core/module-chatstudio.js", + "tier": "core-bundle", + "category": "ai", + "desc": "Toggle the ChatStudio overlay (inside: /setapi, /setmodel, /ask, /help)", + "builtin": false, + "example": "chat" + }, + "checkpoint": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "checkpoint [name] — Create a VFS snapshot (name defaults to ISO timestamp)", + "builtin": false, + "example": "checkpoint" + }, + "checkpoint-del": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "checkpoint-del <name> — Delete a VFS checkpoint", + "builtin": false, + "example": "checkpoint-del" + }, + "checkpoint-list": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "checkpoint-list — List all VFS checkpoints", + "builtin": false, + "example": "checkpoint-list" + }, + "clear": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "Extracted from newOS4 index.html — WebX11Terminal", + "builtin": true, + "example": "clear" + }, + "cmd-allow": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "Extracted from newOS4 index.html — WebX11Terminal", + "builtin": true, + "example": "cmd-allow" + }, + "cmd-deny": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "Extracted from newOS4 index.html — WebX11Terminal", + "builtin": true, + "example": "cmd-deny" + }, + "commit-approve": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "commit-approve — command from module-aiassist", + "builtin": false, + "example": "commit-approve" + }, + "commit-deny": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "commit-deny — command from module-aiassist", + "builtin": false, + "example": "commit-deny" + }, + "commit-status": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "commit-status — command from module-aiassist", + "builtin": false, + "example": "commit-status" + }, + "config": { + "module": "module-config", + "file": "modules/core/module-config.js", + "tier": "core-bundle", + "category": "system", + "desc": "config [key] — Show all config or a specific key", + "builtin": false, + "example": "config" + }, + "config-delete": { + "module": "module-config", + "file": "modules/core/module-config.js", + "tier": "core-bundle", + "category": "system", + "desc": "config-delete <key> — Delete a configuration key", + "builtin": false, + "example": "config-delete" + }, + "config-export": { + "module": "module-config", + "file": "modules/core/module-config.js", + "tier": "core-bundle", + "category": "system", + "desc": "config-export — Export all config as JSON", + "builtin": false, + "example": "config-export" + }, + "config-list": { + "module": "module-config", + "file": "modules/core/module-config.js", + "tier": "core-bundle", + "category": "system", + "desc": "config-list — List all configuration keys", + "builtin": false, + "example": "config-list" + }, + "config-reset": { + "module": "module-config", + "file": "modules/core/module-config.js", + "tier": "core-bundle", + "category": "system", + "desc": "config-reset — Reset all configuration to defaults", + "builtin": false, + "example": "config-reset" + }, + "config-set": { + "module": "module-config", + "file": "modules/core/module-config.js", + "tier": "core-bundle", + "category": "system", + "desc": "config-set <key> <value> — Set a configuration value", + "builtin": false, + "example": "config-set" + }, + "consensus": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "consensus <question> — Start a consensus round: spawn 3 agents, debate the question, resolve by majority", + "builtin": false, + "example": "consensus" + }, + "consensus-history": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "consensus-history — Show past consensus rounds and their resolutions", + "builtin": false, + "example": "consensus-history" + }, + "consensus-status": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "consensus-status — Show current/active consensus round status", + "builtin": false, + "example": "consensus-status" + }, + "context": { + "module": "module-smartctx", + "file": "modules/core/module-smartctx.js", + "tier": "core-bundle", + "category": "ai", + "desc": "context [name] — Set or view current context profile", + "builtin": false, + "example": "context" + }, + "context-auto": { + "module": "module-smartctx", + "file": "modules/core/module-smartctx.js", + "tier": "core-bundle", + "category": "ai", + "desc": "context-auto [on|off] — Toggle auto-routing (detects intent from user input)", + "builtin": false, + "example": "context-auto" + }, + "context-delete": { + "module": "module-smartctx", + "file": "modules/core/module-smartctx.js", + "tier": "core-bundle", + "category": "ai", + "desc": "context-delete <name> — Delete a context profile", + "builtin": false, + "example": "context-delete" + }, + "context-new": { + "module": "module-smartctx", + "file": "modules/core/module-smartctx.js", + "tier": "core-bundle", + "category": "ai", + "desc": "context-new <name> <displayName> <systemPrompt> — Create a new context profile", + "builtin": false, + "example": "context-new" + }, + "contexts": { + "module": "module-smartctx", + "file": "modules/core/module-smartctx.js", + "tier": "core-bundle", + "category": "ai", + "desc": "contexts — List all available context profiles", + "builtin": false, + "example": "contexts" + }, + "contract-list": { + "module": "module-agent-registry", + "file": "modules/core/module-agent-registry.js", + "tier": "core-bundle", + "category": "agents", + "desc": "module-agent-registry.js — VFS-backed agent directory with A2A contracts", + "builtin": false, + "example": "contract-list" + }, + "contract-register": { + "module": "module-agent-registry", + "file": "modules/core/module-agent-registry.js", + "tier": "core-bundle", + "category": "agents", + "desc": "module-agent-registry.js — VFS-backed agent directory with A2A contracts", + "builtin": false, + "example": "contract-register" + }, + "cp": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "cp <source> <dest> — Copy file or directory", + "builtin": false, + "example": "cp" + }, + "cron-clear": { + "module": "cron", + "file": "modules/ext/cron.js", + "tier": "ext-auto", + "category": "automation", + "desc": "cron-clear — Stop all cron jobs", + "builtin": false, + "example": "cron-clear" + }, + "cron-every": { + "module": "cron", + "file": "modules/ext/cron.js", + "tier": "ext-auto", + "category": "automation", + "desc": "cron-every <seconds> <command> — Schedule a command to run every N seconds", + "builtin": false, + "example": "cron-every" + }, + "cron-list": { + "module": "cron", + "file": "modules/ext/cron.js", + "tier": "ext-auto", + "category": "automation", + "desc": "cron-list — List all active cron jobs", + "builtin": false, + "example": "cron-list" + }, + "cron-stop": { + "module": "cron", + "file": "modules/ext/cron.js", + "tier": "ext-auto", + "category": "automation", + "desc": "cron-stop <id> — Stop a specific cron job", + "builtin": false, + "example": "cron-stop" + }, + "cut": { + "module": "busybox", + "file": "modules/ext/busybox.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "cut -f<N> [-d<delim>] <file> — Extract column from file", + "builtin": false, + "example": "cut" + }, + "dashboard": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "dashboard — command from terminal", + "builtin": true, + "example": "dashboard" + }, + "date": { + "module": "busybox", + "file": "modules/ext/busybox.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "date — Show current date/time (ISO, local, UTC, timestamp)", + "builtin": false, + "example": "date" + }, + "debug": { + "module": "debug-panel", + "file": "modules/ext/debug-panel.js", + "tier": "ext-auto", + "category": "dev", + "desc": "debug — Toggle the debug console panel", + "builtin": false, + "example": "debug" + }, + "debug-ai": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "debug-ai — Toggle AI tool-call debug panel", + "builtin": false, + "example": "debug-ai" + }, + "debug-ai-last": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "debug-ai-last — command from module-aiassist", + "builtin": false, + "example": "debug-ai-last" + }, + "debug-ai-log": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "debug-ai-log — command from module-aiassist", + "builtin": false, + "example": "debug-ai-log" + }, + "debug-ai-send": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "debug-ai-send — command from module-aiassist", + "builtin": false, + "example": "debug-ai-send" + }, + "debug-analyze": { + "module": "module-debug-supervisor", + "file": "modules/core/module-debug-supervisor.js", + "tier": "core-bundle", + "category": "general", + "desc": "debug-analyze <hash> — Deep analysis of a specific error", + "builtin": false, + "example": "debug-analyze" + }, + "debug-fix": { + "module": "module-debug-supervisor", + "file": "modules/core/module-debug-supervisor.js", + "tier": "core-bundle", + "category": "general", + "desc": "debug-fix <hash> — Suggest and optionally apply fix for an error", + "builtin": false, + "example": "debug-fix" + }, + "debug-health": { + "module": "module-debug-supervisor", + "file": "modules/core/module-debug-supervisor.js", + "tier": "core-bundle", + "category": "general", + "desc": "debug-health — Check supervisor module health status", + "builtin": false, + "example": "debug-health" + }, + "debug-history": { + "module": "module-debug-supervisor", + "file": "modules/core/module-debug-supervisor.js", + "tier": "core-bundle", + "category": "general", + "desc": "debug-history [n] — Show last N errors (default: 20)", + "builtin": false, + "example": "debug-history" + }, + "debug-stats": { + "module": "module-debug-supervisor", + "file": "modules/core/module-debug-supervisor.js", + "tier": "core-bundle", + "category": "general", + "desc": "debug-stats — Show error summary (total, unique, clusters, top issues)", + "builtin": false, + "example": "debug-stats" + }, + "debug-sync": { + "module": "module-debug-supervisor", + "file": "modules/core/module-debug-supervisor.js", + "tier": "core-bundle", + "category": "general", + "desc": "debug-sync [url] — Sync errors to backend API (or set sync URL)", + "builtin": false, + "example": "debug-sync" + }, + "debug-sync-url": { + "module": "module-debug-supervisor", + "file": "modules/core/module-debug-supervisor.js", + "tier": "core-bundle", + "category": "general", + "desc": "debug-sync-url [url] — View or set the backend sync endpoint", + "builtin": false, + "example": "debug-sync-url" + }, + "debug-trends": { + "module": "module-debug-supervisor", + "file": "modules/core/module-debug-supervisor.js", + "tier": "core-bundle", + "category": "general", + "desc": "debug-trends [days] — Error frequency over time (default: 7)", + "builtin": false, + "example": "debug-trends" + }, + "decrypt": { + "module": "cryptogzip", + "file": "modules/ext/cryptogzip.js", + "tier": "ext-auto", + "category": "system", + "desc": "decrypt <path.enc> <passphrase> — Decrypt a .enc file", + "builtin": false, + "example": "decrypt" + }, + "diff": { + "module": "diff", + "file": "modules/ext/diff.js", + "tier": "critical-ext-bundle", + "category": "editor", + "desc": "Show differences between two VFS files or compare module source (usage: diff <file1> <file2> or diff --module <name>)", + "builtin": false, + "example": "diff" + }, + "dirname": { + "module": "busybox", + "file": "modules/ext/busybox.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "dirname <path> — Strip filename from path", + "builtin": false, + "example": "dirname" + }, + "doctor": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "doctor — Run all health checks, rank issues, show top 3 warnings", + "builtin": false, + "example": "doctor" + }, + "download": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "download <file> — Download a file to your device", + "builtin": false, + "example": "download" + }, + "du": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "du [path] — Estimate file/directory size", + "builtin": false, + "example": "du" + }, + "echo": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "echo <text...> — Print text to the terminal", + "builtin": false, + "example": "echo" + }, + "edit": { + "module": "editor", + "file": "modules/ext/editor.js", + "tier": "critical-ext-bundle", + "category": "editor", + "desc": "Open the code editor for a VFS file (usage: edit <path>)", + "builtin": false, + "example": "edit" + }, + "empty-trash": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "empty-trash — Permanently delete all trash items", + "builtin": false, + "example": "empty-trash" + }, + "encrypt": { + "module": "cryptogzip", + "file": "modules/ext/cryptogzip.js", + "tier": "ext-auto", + "category": "system", + "desc": "encrypt <path> <passphrase> — AES-256-GCM encrypt a file", + "builtin": false, + "example": "encrypt" + }, + "env": { + "module": "busybox", + "file": "modules/ext/busybox.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "env — List environment variables (webx11_ keys + user profile)", + "builtin": false, + "example": "env" + }, + "errors": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "errors [n] — Show last n ERROR log entries (default: 20)", + "builtin": false, + "example": "errors" + }, + "expand": { + "module": "busybox", + "file": "modules/ext/busybox.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "expand <file> — Convert tabs to spaces (default 4) in file", + "builtin": false, + "example": "expand" + }, + "export": { + "module": "shell", + "file": "modules/ext/shell.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "export <var>=<value> — Set a shell variable", + "builtin": false, + "example": "export" + }, + "extract": { + "module": "module-fetcher", + "file": "modules/core/module-fetcher.js", + "tier": "core-bundle", + "category": "web", + "desc": "extract <url> — Extract readable article content (title + body text) from a URL", + "builtin": false, + "example": "extract" + }, + "factory-reset": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "factory-reset — Alias for reset all", + "builtin": false, + "example": "factory-reset" + }, + "factory-reset-old": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "factory-reset-old — command from module-system", + "builtin": false, + "example": "factory-reset-old" + }, + "fetch": { + "module": "module-fetcher", + "file": "modules/core/module-fetcher.js", + "tier": "core-bundle", + "category": "web", + "desc": "fetch <url> [--post=BODY] [--type=CT] [--raw] — Fetch URL (raw HTML/text); --post enables POST with body, --type sets Content-Type, --raw shows untruncated content", + "builtin": false, + "example": "fetch" + }, + "files": { + "module": "filemgr", + "file": "modules/ext/filemgr.js", + "tier": "critical-ext-bundle", + "category": "filesystem", + "desc": "files — Open the file manager overlay with VFS directory listing, preview, encrypt, zip", + "builtin": false, + "example": "files" + }, + "find": { + "module": "finder", + "file": "modules/ext/finder.js", + "tier": "critical-ext-bundle", + "category": "filesystem", + "desc": "Recursively search VFS files/directories by name (usage: find <query>)", + "builtin": false, + "example": "find" + }, + "floating-add": { + "module": "floating", + "file": "modules/ext/floating.js", + "tier": "ext-auto", + "category": "ui", + "desc": "floating-add btn|menu|panel <id> <label> [cmd|btn:Label:cmd...] — Add a floating UI element", + "builtin": false, + "example": "floating-add" + }, + "floating-clear": { + "module": "floating", + "file": "modules/ext/floating.js", + "tier": "ext-auto", + "category": "ui", + "desc": "floating-clear — Remove all floating elements", + "builtin": false, + "example": "floating-clear" + }, + "floating-list": { + "module": "floating", + "file": "modules/ext/floating.js", + "tier": "ext-auto", + "category": "ui", + "desc": "floating-list — Show all floating elements", + "builtin": false, + "example": "floating-list" + }, + "floating-pos": { + "module": "floating", + "file": "modules/ext/floating.js", + "tier": "ext-auto", + "category": "ui", + "desc": "floating-pos <id> <x|auto> <y|auto> — Reposition a floating element", + "builtin": false, + "example": "floating-pos" + }, + "floating-remove": { + "module": "floating", + "file": "modules/ext/floating.js", + "tier": "ext-auto", + "category": "ui", + "desc": "floating-remove <id> — Remove a floating element", + "builtin": false, + "example": "floating-remove" + }, + "floating-toggle": { + "module": "floating", + "file": "modules/ext/floating.js", + "tier": "ext-auto", + "category": "ui", + "desc": "floating-toggle <id> — Show/hide a floating element", + "builtin": false, + "example": "floating-toggle" + }, + "font-size": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "font-size <8-32> — Set terminal font size (persists across sessions)", + "builtin": false, + "example": "font-size" + }, + "grep": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "grep <pattern> [file] — Search file content for pattern", + "builtin": false, + "example": "grep" + }, + "guardrail": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "guardrail — command from module-aiassist", + "builtin": false, + "example": "guardrail" + }, + "guardrail-add": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "guardrail-add — command from module-aiassist", + "builtin": false, + "example": "guardrail-add" + }, + "guardrail-remove": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "guardrail-remove — command from module-aiassist", + "builtin": false, + "example": "guardrail-remove" + }, + "gui": { + "module": "module-simplegui", + "file": "modules/core/module-simplegui.js", + "tier": "core-bundle", + "category": "general", + "desc": "gui — Show the GUI builder overview", + "builtin": false, + "example": "gui" + }, + "gui-button": { + "module": "module-simplegui", + "file": "modules/core/module-simplegui.js", + "tier": "core-bundle", + "category": "general", + "desc": "gui-button <label> <command> — Add a clickable button", + "builtin": false, + "example": "gui-button" + }, + "gui-clear": { + "module": "module-simplegui", + "file": "modules/core/module-simplegui.js", + "tier": "core-bundle", + "category": "general", + "desc": "gui-clear — Clear all GUI elements", + "builtin": false, + "example": "gui-clear" + }, + "gui-input": { + "module": "module-simplegui", + "file": "modules/core/module-simplegui.js", + "tier": "core-bundle", + "category": "general", + "desc": "gui-input <placeholder> <varName> — Add a text input field", + "builtin": false, + "example": "gui-input" + }, + "gui-run": { + "module": "module-simplegui", + "file": "modules/core/module-simplegui.js", + "tier": "core-bundle", + "category": "general", + "desc": "gui-run <command> — Run a terminal command from the GUI", + "builtin": false, + "example": "gui-run" + }, + "gui-text": { + "module": "module-simplegui", + "file": "modules/core/module-simplegui.js", + "tier": "core-bundle", + "category": "general", + "desc": "gui-text <content> — Add a text element", + "builtin": false, + "example": "gui-text" + }, + "gunzip": { + "module": "cryptogzip", + "file": "modules/ext/cryptogzip.js", + "tier": "ext-auto", + "category": "system", + "desc": "gunzip <path.gz> — Decompress a .gz file (binary-safe)", + "builtin": false, + "example": "gunzip" + }, + "gzip": { + "module": "cryptogzip", + "file": "modules/ext/cryptogzip.js", + "tier": "ext-auto", + "category": "system", + "desc": "gzip <path> — Compress a VFS file to .gz", + "builtin": false, + "example": "gzip" + }, + "head": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "head <file> [lines] — Show first N lines (default: 10)", + "builtin": false, + "example": "head" + }, + "health-check": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "health-check — Audit VFS size, modules, logs, trash, skills, tool failures", + "builtin": false, + "example": "health-check" + }, + "help": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "help [command] — Show all commands or usage for a specific command", + "builtin": false, + "example": "help" + }, + "history": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "history — command from terminal", + "builtin": true, + "example": "history" + }, + "hostname": { + "module": "busybox", + "file": "modules/ext/busybox.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "hostname — Show host/user-agent info", + "builtin": false, + "example": "hostname" + }, + "images": { + "module": "module-searxng", + "file": "modules/core/module-searxng.js", + "tier": "core-bundle", + "category": "web", + "desc": "images <query> — Search for images", + "builtin": false, + "example": "images" + }, + "js": { + "module": "console", + "file": "modules/ext/console.js", + "tier": "ext-auto", + "category": "dev", + "desc": "js <expression> — Evaluate JavaScript in page context", + "builtin": false, + "example": "js" + }, + "js-run": { + "module": "console", + "file": "modules/ext/console.js", + "tier": "ext-auto", + "category": "dev", + "desc": "js-run <path> — Load and execute a JS file from VFS", + "builtin": false, + "example": "js-run" + }, + "kanban": { + "module": "kanban", + "file": "modules/ext/kanban.js", + "tier": "ext-auto", + "category": "automation", + "desc": "kanban <command> [args] — Manage tasks on a kanban board (todo, doing, done columns)", + "builtin": false, + "example": "kanban" + }, + "learn-skill": { + "module": "skill-pack-core", + "file": "modules/ext/skill-pack-core.js", + "tier": "ext-auto", + "category": "ai", + "desc": "learn-skill <topic> — Reflect on topic, draft a skill from experience", + "builtin": false, + "example": "learn-skill" + }, + "lease-create": { + "module": "module-leases", + "file": "modules/core/module-leases.js", + "tier": "core-bundle", + "category": "agents", + "desc": "module-leases.js — Lease system for agent task delegation", + "builtin": false, + "example": "lease-create" + }, + "lease-list": { + "module": "module-leases", + "file": "modules/core/module-leases.js", + "tier": "core-bundle", + "category": "agents", + "desc": "module-leases.js — Lease system for agent task delegation", + "builtin": false, + "example": "lease-list" + }, + "lease-renew": { + "module": "module-leases", + "file": "modules/core/module-leases.js", + "tier": "core-bundle", + "category": "agents", + "desc": "lease-renew — command from module-leases", + "builtin": false, + "example": "lease-renew" + }, + "lease-revoke": { + "module": "module-leases", + "file": "modules/core/module-leases.js", + "tier": "core-bundle", + "category": "agents", + "desc": "module-leases.js — Lease system for agent task delegation", + "builtin": false, + "example": "lease-revoke" + }, + "listmods": { + "module": "module-modmgr", + "file": "modules/core/module-modmgr.js", + "tier": "core-bundle", + "category": "general", + "desc": "listmods — List all loaded modules with status", + "builtin": false, + "example": "listmods" + }, + "load-local": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "Extracted from newOS4 index.html — ModuleManager", + "builtin": true, + "example": "load-local" + }, + "loadlocal": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "Extracted from newOS4 index.html — ModuleManager", + "builtin": true, + "example": "loadlocal" + }, + "loadmod": { + "module": "module-modmgr", + "file": "modules/core/module-modmgr.js", + "tier": "core-bundle", + "category": "general", + "desc": "loadmod <url> — Load a module from a URL", + "builtin": false, + "example": "loadmod" + }, + "logrotate": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "logrotate — Rotate current.log to a dated archive (auto-cleans old)", + "builtin": false, + "example": "logrotate" + }, + "logs": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "logs [n] [LEVEL] — Show last n log entries (optionally filtered by level: INFO/WARN/ERROR)", + "builtin": false, + "example": "logs" + }, + "ls": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "ls [path] [-la] — List files and directories", + "builtin": false, + "example": "ls" + }, + "ls-trash": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "ls-trash — List items in trash", + "builtin": false, + "example": "ls-trash" + }, + "mem": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "mem — Show memory usage (heap + localStorage + VFS)", + "builtin": false, + "example": "mem" + }, + "mem-debug": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "mem-debug — Show memory breakdown by component", + "builtin": false, + "example": "mem-debug" + }, + "memgraph-agent": { + "module": "module-memory-graph", + "file": "modules/core/module-memory-graph.js", + "tier": "core-bundle", + "category": "ai", + "desc": "memgraph-agent <agentId> — Show namespaced nodes for an agent", + "builtin": false, + "example": "memgraph-agent" + }, + "memgraph-decay-config": { + "module": "module-memory-graph", + "file": "modules/core/module-memory-graph.js", + "tier": "core-bundle", + "category": "ai", + "desc": "memgraph-decay-config <days> <min_uses> — Configure decay policy (default: 30d, 3 uses)", + "builtin": false, + "example": "memgraph-decay-config" + }, + "memgraph-export": { + "module": "module-memory-graph", + "file": "modules/core/module-memory-graph.js", + "tier": "core-bundle", + "category": "ai", + "desc": "memgraph-export — Export graph as JSON", + "builtin": false, + "example": "memgraph-export" + }, + "memgraph-learn": { + "module": "module-memory-graph", + "file": "modules/core/module-memory-graph.js", + "tier": "core-bundle", + "category": "ai", + "desc": "memgraph-learn <entity> <fact> — Add a fact to the memory graph", + "builtin": false, + "example": "memgraph-learn" + }, + "memgraph-prune": { + "module": "module-memory-graph", + "file": "modules/core/module-memory-graph.js", + "tier": "core-bundle", + "category": "ai", + "desc": "memgraph-prune — Remove stale low-weight edges (>30d, <3 accesses)", + "builtin": false, + "example": "memgraph-prune" + }, + "memgraph-search": { + "module": "module-memory-graph", + "file": "modules/core/module-memory-graph.js", + "tier": "core-bundle", + "category": "ai", + "desc": "memgraph-search <query> — Find nodes by label or type", + "builtin": false, + "example": "memgraph-search" + }, + "memgraph-session": { + "module": "module-memory-graph", + "file": "modules/core/module-memory-graph.js", + "tier": "core-bundle", + "category": "ai", + "desc": "memgraph-session [date] — Show graph nodes for a session (default: today)", + "builtin": false, + "example": "memgraph-session" + }, + "memgraph-stats": { + "module": "module-memory-graph", + "file": "modules/core/module-memory-graph.js", + "tier": "core-bundle", + "category": "ai", + "desc": "memgraph-stats — Most connected nodes, strongest edges, cluster info", + "builtin": false, + "example": "memgraph-stats" + }, + "memgraph-status": { + "module": "module-memory-graph", + "file": "modules/core/module-memory-graph.js", + "tier": "core-bundle", + "category": "ai", + "desc": "memgraph-status — Show node/edge counts, last access", + "builtin": false, + "example": "memgraph-status" + }, + "memory-map-export": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "memory-map-export — Export memory index to /memory/reference.md (overwrites file)", + "builtin": false, + "example": "memory-map-export" + }, + "mindmap": { + "module": "mindmap", + "file": "modules/ext/mindmap.js", + "tier": "ext-auto", + "category": "general", + "desc": "mindmap <subcommand> [...] — Create, edit, visualize mindmaps. v4.5.44: multi-mindmap, find, move, undo, import, merge, diff, stats, mermaid, refresh-html. Run `mindmap help` for full list.", + "builtin": false, + "example": "mindmap" + }, + "mkdir": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "mkdir <dir> [-p] — Create a directory; -p creates parent paths", + "builtin": false, + "example": "mkdir" + }, + "model-delete": { + "module": "module-ollama", + "file": "modules/core/module-ollama.js", + "tier": "core-bundle", + "category": "ai", + "desc": "model-delete <model-name> — Delete a model from local storage", + "builtin": false, + "example": "model-delete" + }, + "model-info": { + "module": "module-ollama", + "file": "modules/core/module-ollama.js", + "tier": "core-bundle", + "category": "ai", + "desc": "model-info <model-name> — Show details about a specific model", + "builtin": false, + "example": "model-info" + }, + "model-pull": { + "module": "module-ollama", + "file": "modules/core/module-ollama.js", + "tier": "core-bundle", + "category": "ai", + "desc": "model-pull <model-name> — Pull a model from Ollama registry", + "builtin": false, + "example": "model-pull" + }, + "model-run": { + "module": "module-ollama", + "file": "modules/core/module-ollama.js", + "tier": "core-bundle", + "category": "ai", + "desc": "model-run <model-name> [prompt] — Run inference with a model", + "builtin": false, + "example": "model-run" + }, + "models": { + "module": "module-ollama", + "file": "modules/core/module-ollama.js", + "tier": "core-bundle", + "category": "ai", + "desc": "models — List all available Ollama models (table format)", + "builtin": false, + "example": "models" + }, + "module-disable": { + "module": "module-modmgr", + "file": "modules/core/module-modmgr.js", + "tier": "core-bundle", + "category": "general", + "desc": "module-disable <name> — Disable a module from auto-loading on refresh (persists)", + "builtin": false, + "example": "module-disable" + }, + "module-enable": { + "module": "module-modmgr", + "file": "modules/core/module-modmgr.js", + "tier": "core-bundle", + "category": "general", + "desc": "module-enable <name> — Re-enable a previously disabled module", + "builtin": false, + "example": "module-enable" + }, + "module-info": { + "module": "module-modmgr", + "file": "modules/core/module-modmgr.js", + "tier": "core-bundle", + "category": "general", + "desc": "module-info <name> — Show module details: path, status, size, type, source, commands list", + "builtin": false, + "example": "module-info" + }, + "module-list": { + "module": "module-modmgr", + "file": "modules/core/module-modmgr.js", + "tier": "core-bundle", + "category": "general", + "desc": "module-list [flags] — List loaded modules grouped by category. Flags: --flat, --core, --ext, --foundation, --lazy, --eager, --loaded, --unloaded, --search=<pat>, --failed, --disabled", + "builtin": false, + "example": "module-list" + }, + "module-load": { + "module": "module-modmgr", + "file": "modules/core/module-modmgr.js", + "tier": "core-bundle", + "category": "general", + "desc": "module-load <name> — Mark a lazy module as loaded", + "builtin": false, + "example": "module-load" + }, + "module-reload": { + "module": "module-modmgr", + "file": "modules/core/module-modmgr.js", + "tier": "core-bundle", + "category": "general", + "desc": "module-reload <name> — Unload then reload a module (shows diff)", + "builtin": false, + "example": "module-reload" + }, + "module-reset": { + "module": "module-modmgr", + "file": "modules/core/module-modmgr.js", + "tier": "core-bundle", + "category": "general", + "desc": "module-reset <name> — Reset a module to a clean state (clears its state, keeps commands)", + "builtin": false, + "example": "module-reset" + }, + "module-stats": { + "module": "module-modmgr", + "file": "modules/core/module-modmgr.js", + "tier": "core-bundle", + "category": "general", + "desc": "module-stats — Show aggregate module statistics (loaded/unloaded/lazy/failed/disabled + total size)", + "builtin": false, + "example": "module-stats" + }, + "module-unload": { + "module": "module-modmgr", + "file": "modules/core/module-modmgr.js", + "tier": "core-bundle", + "category": "general", + "desc": "module-unload <name> — Mark a module as unloaded", + "builtin": false, + "example": "module-unload" + }, + "modules": { + "module": "module-modmgr", + "file": "modules/core/module-modmgr.js", + "tier": "core-bundle", + "category": "general", + "desc": "modules — Open the module manager GUI", + "builtin": false, + "example": "modules" + }, + "modules-list": { + "module": "module-modmgr", + "file": "modules/core/module-modmgr.js", + "tier": "core-bundle", + "category": "general", + "desc": "modules-list [core|ext] — List all available modules from the module index", + "builtin": false, + "example": "modules-list" + }, + "monitor": { + "module": "module-servermon", + "file": "modules/core/module-servermon.js", + "tier": "core-bundle", + "category": "system", + "desc": "monitor — Toggle auto-monitoring (checks every 60s)", + "builtin": false, + "example": "monitor" + }, + "mv": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "mv <source> <dest> — Move file or directory", + "builtin": false, + "example": "mv" + }, + "node": { + "module": "console", + "file": "modules/ext/console.js", + "tier": "ext-auto", + "category": "dev", + "desc": "node <path> — Alias for js-run (executes a JS file from VFS)", + "builtin": false, + "example": "node" + }, + "nvfs-alias": { + "module": "module-neural-vfs", + "file": "modules/core/module-neural-vfs.js", + "tier": "core-bundle", + "category": "ai", + "desc": "nvfs-alias list|add|remove|clear — Manage concept aliases", + "builtin": false, + "example": "nvfs-alias" + }, + "nvfs-cancel": { + "module": "module-neural-vfs", + "file": "modules/core/module-neural-vfs.js", + "tier": "core-bundle", + "category": "ai", + "desc": "nvfs-cancel — Cancel running index operation", + "builtin": false, + "example": "nvfs-cancel" + }, + "nvfs-config": { + "module": "module-neural-vfs", + "file": "modules/core/module-neural-vfs.js", + "tier": "core-bundle", + "category": "ai", + "desc": "nvfs-config [key] [value] — View or set Neural VFS config (fuzzy-limit, tree-files, tree-concepts, resolve-files, deep-index, deep-model, exclude-patterns, exclude-dirs, exclude-files, include-only)", + "builtin": false, + "example": "nvfs-config" + }, + "nvfs-index": { + "module": "module-neural-vfs", + "file": "modules/core/module-neural-vfs.js", + "tier": "core-bundle", + "category": "ai", + "desc": "nvfs-index [--deep] — Scan VFS and build semantic concept index", + "builtin": false, + "example": "nvfs-index" + }, + "nvfs-index-graph": { + "module": "module-neural-vfs", + "file": "modules/core/module-neural-vfs.js", + "tier": "core-bundle", + "category": "ai", + "desc": "nvfs-index-graph — command from module-neural-vfs", + "builtin": false, + "example": "nvfs-index-graph" + }, + "nvfs-resolve": { + "module": "module-neural-vfs", + "file": "modules/core/module-neural-vfs.js", + "tier": "core-bundle", + "category": "ai", + "desc": "nvfs-resolve <concept> — Find files related to a concept", + "builtin": false, + "example": "nvfs-resolve" + }, + "nvfs-save": { + "module": "module-neural-vfs", + "file": "modules/core/module-neural-vfs.js", + "tier": "core-bundle", + "category": "ai", + "desc": "nvfs-save — Force-save current in-memory index to VFS (survives refresh)", + "builtin": false, + "example": "nvfs-save" + }, + "nvfs-status": { + "module": "module-neural-vfs", + "file": "modules/core/module-neural-vfs.js", + "tier": "core-bundle", + "category": "ai", + "desc": "nvfs-status — Show semantic index stats and last scan time", + "builtin": false, + "example": "nvfs-status" + }, + "nvfs-sync": { + "module": "module-neural-vfs", + "file": "modules/core/module-neural-vfs.js", + "tier": "core-bundle", + "category": "ai", + "desc": "nvfs-sync [--deep] — Incremental background sync (auto-sync dirty files)", + "builtin": false, + "example": "nvfs-sync" + }, + "nvfs-tree": { + "module": "module-neural-vfs", + "file": "modules/core/module-neural-vfs.js", + "tier": "core-bundle", + "category": "ai", + "desc": "nvfs-tree [concept] — Show concept-to-file mapping as a tree", + "builtin": false, + "example": "nvfs-tree" + }, + "ollama": { + "module": "module-ollama", + "file": "modules/core/module-ollama.js", + "tier": "core-bundle", + "category": "ai", + "desc": "ollama — Open the Ollama model manager UI", + "builtin": false, + "example": "ollama" + }, + "ollamaconnect": { + "module": "module-ollama", + "file": "modules/core/module-ollama.js", + "tier": "core-bundle", + "category": "ai", + "desc": "ollamaconnect — Connect to the Ollama server", + "builtin": false, + "example": "ollamaconnect" + }, + "ollamadisconnect": { + "module": "module-ollama", + "file": "modules/core/module-ollama.js", + "tier": "core-bundle", + "category": "ai", + "desc": "ollamadisconnect — Disconnect from the Ollama server", + "builtin": false, + "example": "ollamadisconnect" + }, + "ollamamodels": { + "module": "module-ollama", + "file": "modules/core/module-ollama.js", + "tier": "core-bundle", + "category": "ai", + "desc": "ollamamodels — List all available models from Ollama", + "builtin": false, + "example": "ollamamodels" + }, + "ollamasetmodel": { + "module": "module-ollama", + "file": "modules/core/module-ollama.js", + "tier": "core-bundle", + "category": "ai", + "desc": "ollamasetmodel <name> — Set the active Ollama model", + "builtin": false, + "example": "ollamasetmodel" + }, + "ollamasetproxy": { + "module": "module-ollama", + "file": "modules/core/module-ollama.js", + "tier": "core-bundle", + "category": "ai", + "desc": "ollamasetproxy [endpoint] — View or change the Ollama endpoint URL", + "builtin": false, + "example": "ollamasetproxy" + }, + "orchestrate": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "orchestrate <task> — Delegate a task to an available agent (auto-spawns if needed)", + "builtin": false, + "example": "orchestrate" + }, + "orchestrate-decompose": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "orchestrate-decompose <task> — Preview how a task would be decomposed into sub-tasks", + "builtin": false, + "example": "orchestrate-decompose" + }, + "orchestrate-parallel": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "orchestrate-parallel <task> — Decompose and delegate sub-tasks in parallel (auto-splits on \"and\"/\"then\")", + "builtin": false, + "example": "orchestrate-parallel" + }, + "orchestrate-result": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "orchestrate-result — Show the last parallel batch results (stored in VFS)", + "builtin": false, + "example": "orchestrate-result" + }, + "orchestrator-config": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "orchestrator-config <key> <value> — Tune: spawn-limit, spawn-strategy, heartbeat-timeout, idle-timeout, restart-policy, a2a-polling, a2a-poll-interval, heartbeat-check, idle-sweep-interval, dead-purge-timeout", + "builtin": false, + "example": "orchestrator-config" + }, + "orchestrator-kill": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "orchestrator-kill <agent-id> — Terminate an agent and revoke leases", + "builtin": false, + "example": "orchestrator-kill" + }, + "orchestrator-list": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "orchestrator-list — List all spawned agents with full details", + "builtin": false, + "example": "orchestrator-list" + }, + "orchestrator-purge": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "orchestrator-purge — Remove dead agents (>60s since heartbeat)", + "builtin": false, + "example": "orchestrator-purge" + }, + "orchestrator-restart-policy": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "orchestrator-restart-policy <agent-id> <on-failure|always|never> — Set per-agent restart policy", + "builtin": false, + "example": "orchestrator-restart-policy" + }, + "orchestrator-schedule": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "orchestrator-schedule <role> <interval_seconds> <task...> — Schedule a recurring task delegation", + "builtin": false, + "example": "orchestrator-schedule" + }, + "orchestrator-schedules": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "orchestrator-schedules — List all active recurring schedules", + "builtin": false, + "example": "orchestrator-schedules" + }, + "orchestrator-sleep": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "orchestrator-sleep <agent-id> — Suspend agent to VFS", + "builtin": false, + "example": "orchestrator-sleep" + }, + "orchestrator-spawn": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "orchestrator-spawn [role] [capabilities...] — Manually spawn a new agent", + "builtin": false, + "example": "orchestrator-spawn" + }, + "orchestrator-status": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "orchestrator-status — Show agent pool status and config", + "builtin": false, + "example": "orchestrator-status" + }, + "orchestrator-unschedule": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "orchestrator-unschedule <id> — Remove a recurring schedule", + "builtin": false, + "example": "orchestrator-unschedule" + }, + "orchestrator-wake": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "orchestrator-wake <agent-id> — Restore agent from VFS sleep", + "builtin": false, + "example": "orchestrator-wake" + }, + "pipeline": { + "module": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "category": "agents", + "desc": "pipeline <test|deploy|verify|history|config> — Autonomous CI/CD: test agents/queue/modules, deploy (test→auto-fix→verify), history (show runs), config (show config)", + "builtin": false, + "example": "pipeline" + }, + "predict": { + "module": "module-predictor", + "file": "modules/core/module-predictor.js", + "tier": "core-bundle", + "category": "general", + "desc": "predict <task description> — Predict task duration, tool turns, and confidence", + "builtin": false, + "example": "predict" + }, + "predictor-config": { + "module": "module-predictor", + "file": "modules/core/module-predictor.js", + "tier": "core-bundle", + "category": "general", + "desc": "predictor-config <key> <value> — Tune: confidence-threshold, min-data-points", + "builtin": false, + "example": "predictor-config" + }, + "predictor-status": { + "module": "module-predictor", + "file": "modules/core/module-predictor.js", + "tier": "core-bundle", + "category": "general", + "desc": "predictor-status — Model accuracy, data points, prediction count", + "builtin": false, + "example": "predictor-status" + }, + "preview": { + "module": "filemgr", + "file": "modules/ext/filemgr.js", + "tier": "critical-ext-bundle", + "category": "filesystem", + "desc": "preview <path> — Preview a specific file", + "builtin": false, + "example": "preview" + }, + "printf": { + "module": "busybox", + "file": "modules/ext/busybox.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "printf <format> [args...] — Formatted print (%s, %d, %f, \\\\n, \\\\t)", + "builtin": false, + "example": "printf" + }, + "put": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "put <file> <content> — Create or overwrite a file", + "builtin": false, + "example": "put" + }, + "pwd": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "pwd — Print current working directory", + "builtin": false, + "example": "pwd" + }, + "removemod": { + "module": "module-modmgr", + "file": "modules/core/module-modmgr.js", + "tier": "core-bundle", + "category": "general", + "desc": "removemod <name> — Remove a loaded module by name", + "builtin": false, + "example": "removemod" + }, + "reset": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "reset [mem|vfs|config|all] — Global reset command (default: mem)", + "builtin": false, + "example": "reset" + }, + "reset-mem": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "reset-mem — Reset AI memory (logs, context, profile, guardrails) without losing VFS or config", + "builtin": false, + "example": "reset-mem" + }, + "reset-vfs": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "reset-vfs — Alias for reset vfs", + "builtin": false, + "example": "reset-vfs" + }, + "rm": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "rm <file> — Remove a file (moved to trash); use -r/-rf for directories", + "builtin": false, + "example": "rm" + }, + "rmdir": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "rmdir <dir> — Remove a directory", + "builtin": false, + "example": "rmdir" + }, + "rollback": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "rollback <name> — Restore VFS from a checkpoint", + "builtin": false, + "example": "rollback" + }, + "s": { + "module": "module-searxng", + "file": "modules/core/module-searxng.js", + "tier": "core-bundle", + "category": "web", + "desc": "s — command from module-searxng", + "builtin": false, + "example": "s" + }, + "sandbox-config": { + "module": "module-sandbox", + "file": "modules/core/module-sandbox.js", + "tier": "core-bundle", + "category": "ai", + "desc": "sandbox-config <key> <value> — Tune: max-workers, min-workers, pool-timeout", + "builtin": false, + "example": "sandbox-config" + }, + "sandbox-run": { + "module": "module-sandbox", + "file": "modules/core/module-sandbox.js", + "tier": "core-bundle", + "category": "ai", + "desc": "sandbox-run <task> — Run a task in the sandbox (automatic routing)", + "builtin": false, + "example": "sandbox-run" + }, + "sandbox-status": { + "module": "module-sandbox", + "file": "modules/core/module-sandbox.js", + "tier": "core-bundle", + "category": "ai", + "desc": "sandbox-status — Show sandbox pool health and task metrics", + "builtin": false, + "example": "sandbox-status" + }, + "script": { + "module": "shell", + "file": "modules/ext/shell.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "script <path.sh> — Run a shell script from VFS", + "builtin": false, + "example": "script" + }, + "search": { + "module": "module-searxng", + "file": "modules/core/module-searxng.js", + "tier": "core-bundle", + "category": "web", + "desc": "search <query> [--summarize/-s] [--fetch/-f] [--images/-i] [--videos/-v] — Search using SearXNG; --summarize/-s adds AI overview, --fetch/-f fetches page content, --images/-i searches for images, --videos/-v searches for videos", + "builtin": false, + "example": "search" + }, + "secret-del": { + "module": "secret-vault", + "file": "modules/core/secret-vault.js", + "tier": "core-bundle", + "category": "general", + "desc": "secret-del <name> — Remove a stored secret", + "builtin": false, + "example": "secret-del" + }, + "secret-get": { + "module": "secret-vault", + "file": "modules/core/secret-vault.js", + "tier": "core-bundle", + "category": "general", + "desc": "secret-get <name> — Decrypt and display a secret value", + "builtin": false, + "example": "secret-get" + }, + "secret-list": { + "module": "secret-vault", + "file": "modules/core/secret-vault.js", + "tier": "core-bundle", + "category": "general", + "desc": "secret-list — List all stored secret names", + "builtin": false, + "example": "secret-list" + }, + "secret-lock": { + "module": "secret-vault", + "file": "modules/core/secret-vault.js", + "tier": "core-bundle", + "category": "general", + "desc": "secret-lock — Lock the vault (clear derived key from memory)", + "builtin": false, + "example": "secret-lock" + }, + "secret-set": { + "module": "secret-vault", + "file": "modules/core/secret-vault.js", + "tier": "core-bundle", + "category": "general", + "desc": "secret-set <name> <value> — Encrypt and store a secret", + "builtin": false, + "example": "secret-set" + }, + "secret-setpin": { + "module": "secret-vault", + "file": "modules/core/secret-vault.js", + "tier": "core-bundle", + "category": "general", + "desc": "secret-setpin <pin> — Set or change the vault PIN code", + "builtin": false, + "example": "secret-setpin" + }, + "secret-status": { + "module": "secret-vault", + "file": "modules/core/secret-vault.js", + "tier": "core-bundle", + "category": "general", + "desc": "secret-status — Show vault status (locked/unlocked/pin set/open mode)", + "builtin": false, + "example": "secret-status" + }, + "secret-unlock": { + "module": "secret-vault", + "file": "modules/core/secret-vault.js", + "tier": "core-bundle", + "category": "general", + "desc": "secret-unlock <pin> — Unlock the vault with a PIN code", + "builtin": false, + "example": "secret-unlock" + }, + "security": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "Extracted from newOS4 index.html — WebX11Terminal", + "builtin": true, + "example": "security" + }, + "security-destructive": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "security-destructive — command from terminal", + "builtin": true, + "example": "security-destructive" + }, + "security-flag": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "security-flag — command from terminal", + "builtin": true, + "example": "security-flag" + }, + "security-level": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "Extracted from newOS4 index.html — WebX11Terminal", + "builtin": true, + "example": "security-level" + }, + "security-system": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "security-system — command from terminal", + "builtin": true, + "example": "security-system" + }, + "security-whitelist": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "security-whitelist — command from terminal", + "builtin": true, + "example": "security-whitelist" + }, + "sed": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "sed <expr> [file] — Stream editor; s/pat/rep/[g][i][p], /pat/d, Nd, $d; -i write in place; -n quiet mode", + "builtin": false, + "example": "sed" + }, + "seq": { + "module": "busybox", + "file": "modules/ext/busybox.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "seq <last> or seq <first> <last> — Generate sequence of numbers", + "builtin": false, + "example": "seq" + }, + "server": { + "module": "module-servermon", + "file": "modules/core/module-servermon.js", + "tier": "core-bundle", + "category": "system", + "desc": "server <name> <url> — Add a server to monitor", + "builtin": false, + "example": "server" + }, + "server-check": { + "module": "module-servermon", + "file": "modules/core/module-servermon.js", + "tier": "core-bundle", + "category": "system", + "desc": "server-check <name-or-index> — Check a server status", + "builtin": false, + "example": "server-check" + }, + "server-remove": { + "module": "module-servermon", + "file": "modules/core/module-servermon.js", + "tier": "core-bundle", + "category": "system", + "desc": "server-remove <name-or-index> — Remove a server", + "builtin": false, + "example": "server-remove" + }, + "servers": { + "module": "module-servermon", + "file": "modules/core/module-servermon.js", + "tier": "core-bundle", + "category": "system", + "desc": "servers — List all monitored servers", + "builtin": false, + "example": "servers" + }, + "session-delete": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "session-delete <name> — Delete a saved session", + "builtin": false, + "example": "session-delete" + }, + "session-export": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "session-export — command from module-fs", + "builtin": false, + "example": "session-export" + }, + "session-import": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "session-import — command from module-fs", + "builtin": false, + "example": "session-import" + }, + "session-list": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "session-list — List all saved sessions", + "builtin": false, + "example": "session-list" + }, + "session-load": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "session-load <name> — Load a saved session", + "builtin": false, + "example": "session-load" + }, + "session-new": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "session-new — Start a fresh session (auto-saves current, clears AI context)", + "builtin": false, + "example": "session-new" + }, + "session-save": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "session-save <name> — Save current session (path + terminal history)", + "builtin": false, + "example": "session-save" + }, + "set": { + "module": "shell", + "file": "modules/ext/shell.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "set — List all shell variables", + "builtin": false, + "example": "set" + }, + "setfont": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "setfont <8-32> — Alias for font-size", + "builtin": false, + "example": "setfont" + }, + "sh": { + "module": "shell", + "file": "modules/ext/shell.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "sh <path.sh> — Run a shell script from VFS (alias for script)", + "builtin": false, + "example": "sh" + }, + "shadow": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "shadow — command from module-aiassist", + "builtin": false, + "example": "shadow" + }, + "shadow-commit": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "shadow-commit — command from module-aiassist", + "builtin": false, + "example": "shadow-commit" + }, + "shadow-rollback": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "shadow-rollback — command from module-aiassist", + "builtin": false, + "example": "shadow-rollback" + }, + "skill-audit": { + "module": "skill-pack-core", + "file": "modules/ext/skill-pack-core.js", + "tier": "ext-auto", + "category": "ai", + "desc": "skill-audit — Audit skills for stale, duplicate, and broken references", + "builtin": false, + "example": "skill-audit" + }, + "skill-create": { + "module": "skill-pack-core", + "file": "modules/ext/skill-pack-core.js", + "tier": "ext-auto", + "category": "ai", + "desc": "skill-create <name> <desc> \"<steps>\" [tags] — Create a new skill", + "builtin": false, + "example": "skill-create" + }, + "skill-delete": { + "module": "skill-pack-core", + "file": "modules/ext/skill-pack-core.js", + "tier": "ext-auto", + "category": "ai", + "desc": "skill-delete <name> — Delete a skill", + "builtin": false, + "example": "skill-delete" + }, + "skill-export": { + "module": "skill-pack-core", + "file": "modules/ext/skill-pack-core.js", + "tier": "ext-auto", + "category": "ai", + "desc": "skill-export <name> — Export a skill as JSON", + "builtin": false, + "example": "skill-export" + }, + "skill-import": { + "module": "skill-pack-core", + "file": "modules/ext/skill-pack-core.js", + "tier": "ext-auto", + "category": "ai", + "desc": "skill-import <json> — Import a skill from JSON string", + "builtin": false, + "example": "skill-import" + }, + "skill-list": { + "module": "skill-pack-core", + "file": "modules/ext/skill-pack-core.js", + "tier": "ext-auto", + "category": "ai", + "desc": "skill-list [category] — List all skills, optionally filtered by category", + "builtin": false, + "example": "skill-list" + }, + "skill-pack-core": { + "module": "skill-pack-core", + "file": "modules/ext/skill-pack-core.js", + "tier": "ext-auto", + "category": "ai", + "desc": "Seed and manage built-in core skills (navigation, memory, search, modules, backup, agents, webdav, security, etc.)", + "builtin": false, + "example": "skill-pack-core" + }, + "skill-patch": { + "module": "skill-pack-core", + "file": "modules/ext/skill-pack-core.js", + "tier": "ext-auto", + "category": "ai", + "desc": "skill-patch <name> \"<content>\" — Replace a skill's full content", + "builtin": false, + "example": "skill-patch" + }, + "skill-restore": { + "module": "skill-pack-core", + "file": "modules/ext/skill-pack-core.js", + "tier": "ext-auto", + "category": "ai", + "desc": "skill-restore <name> — Restore an archived skill", + "builtin": false, + "example": "skill-restore" + }, + "skill-sync": { + "module": "skill-pack-core", + "file": "modules/ext/skill-pack-core.js", + "tier": "ext-auto", + "category": "ai", + "desc": "skill-sync — Rebuild skills index from /skills/ directory", + "builtin": false, + "example": "skill-sync" + }, + "skill-validate": { + "module": "skill-pack-core", + "file": "modules/ext/skill-pack-core.js", + "tier": "ext-auto", + "category": "ai", + "desc": "skill-validate <name> — Validate skill structure and get confidence score", + "builtin": false, + "example": "skill-validate" + }, + "skill-view": { + "module": "skill-pack-core", + "file": "modules/ext/skill-pack-core.js", + "tier": "ext-auto", + "category": "ai", + "desc": "skill-view <name> — View a skill's full content", + "builtin": false, + "example": "skill-view" + }, + "skill-webdav-sync": { + "module": "skill-pack-core", + "file": "modules/ext/skill-pack-core.js", + "tier": "ext-auto", + "category": "ai", + "desc": "skill-webdav-sync — Push all skills to WebDAV as JSON files", + "builtin": false, + "example": "skill-webdav-sync" + }, + "sleep": { + "module": "busybox", + "file": "modules/ext/busybox.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "sleep <seconds> — Delay execution with countdown", + "builtin": false, + "example": "sleep" + }, + "smart-ask": { + "module": "module-smartctx", + "file": "modules/core/module-smartctx.js", + "tier": "core-bundle", + "category": "ai", + "desc": "smart-ask <question> — Ask a question using the active context", + "builtin": false, + "example": "smart-ask" + }, + "smart-prompt": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "Extracted from newOS4 index.html — SystemModule", + "builtin": true, + "example": "smart-prompt" + }, + "sort": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "sort <file> — Sort lines of a file alphabetically", + "builtin": false, + "example": "sort" + }, + "speak": { + "module": "voice", + "file": "modules/ext/voice.js", + "tier": "ext-auto", + "category": "ui", + "desc": "speak [text] — Text-to-speech. No args = toggle auto-speak for AI responses", + "builtin": false, + "example": "speak" + }, + "startup-modules": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "Extracted from newOS4 index.html — ModuleManager", + "builtin": true, + "example": "startup-modules" + }, + "status": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "status — Display system status (network, files, modules, Ollama)", + "builtin": false, + "example": "status" + }, + "sysmsg": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "sysmsg — command from terminal", + "builtin": true, + "example": "sysmsg" + }, + "system-health": { + "module": "module-servermon", + "file": "modules/core/module-servermon.js", + "tier": "core-bundle", + "category": "system", + "desc": "system-health — Show tool latency, module load times, VFS size, and error rates", + "builtin": false, + "example": "system-health" + }, + "tail": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "tail [-f] [-n N] <file> — Show last N lines; -f follows live changes (Ctrl+C/Esc to stop)", + "builtin": false, + "example": "tail" + }, + "tar": { + "module": "cryptogzip", + "file": "modules/ext/cryptogzip.js", + "tier": "ext-auto", + "category": "system", + "desc": "tar <src-dir> <out.tar[.gz]> — Bundle a VFS directory into a tar archive (gzip if .gz)", + "builtin": false, + "example": "tar" + }, + "test-net": { + "module": "module-servermon", + "file": "modules/core/module-servermon.js", + "tier": "core-bundle", + "category": "system", + "desc": "test-net — Check proxy API, Ollama, and internet connectivity", + "builtin": false, + "example": "test-net" + }, + "theme": { + "module": "theme", + "file": "modules/ext/theme.js", + "tier": "ext-auto", + "category": "ui", + "desc": "theme <set|list|new|delete|export|preview|keep> [...] — Manage and preview visual themes", + "builtin": false, + "example": "theme" + }, + "three": { + "module": "three", + "file": "modules/ext/three.js", + "tier": "ext-auto", + "category": "ui", + "desc": "three <subcommand> [...] — 3D scene builder (Three.js). Run `three help` for full list.", + "builtin": false, + "example": "three" + }, + "touch": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "touch <file> — Create empty file or update timestamp", + "builtin": false, + "example": "touch" + }, + "trash": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "trash <file> — Move a file to trash", + "builtin": false, + "example": "trash" + }, + "trash-dir": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "trash-dir <dir> — Move a directory to trash", + "builtin": false, + "example": "trash-dir" + }, + "tree": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "tree — Display directory structure as a tree", + "builtin": false, + "example": "tree" + }, + "tutorial": { + "module": "tutorial", + "file": "modules/ext/tutorial.js", + "tier": "ext-auto", + "category": "learning", + "desc": "tutorial [start|skip|status] — Interactive guided tour of newOS4 (6 quests)", + "builtin": false, + "example": "tutorial" + }, + "unalias": { + "module": "alias", + "file": "modules/ext/alias.js", + "tier": "critical-ext-bundle", + "category": "general", + "desc": "Remove a user-defined alias (usage: unalias <name>)", + "builtin": false, + "example": "unalias" + }, + "uniq": { + "module": "busybox", + "file": "modules/ext/busybox.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "uniq <file> — Filter adjacent duplicate lines from a file", + "builtin": false, + "example": "uniq" + }, + "unset": { + "module": "shell", + "file": "modules/ext/shell.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "unset <var> — Remove a shell variable", + "builtin": false, + "example": "unset" + }, + "untar": { + "module": "cryptogzip", + "file": "modules/ext/cryptogzip.js", + "tier": "ext-auto", + "category": "system", + "desc": "untar <path.tar[.gz]> [dest-dir] — Extract a tar archive into VFS (auto-detects gzip)", + "builtin": false, + "example": "untar" + }, + "upload": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "upload — Upload a file from your device", + "builtin": false, + "example": "upload" + }, + "uptime": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "uptime — Show time since page load + last save time", + "builtin": false, + "example": "uptime" + }, + "usage": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "usage — command from terminal", + "builtin": true, + "example": "usage" + }, + "vaultmind-connect": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "vaultmind-connect [testnet|mainnet] — Connect VaultMind wallet using hyperliquid_key from SecretVault", + "builtin": false, + "example": "vaultmind-connect" + }, + "vaultmind-query": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "vaultmind-query <prices|portfolio|transactions|copy-trade|chat> [message] — Query VaultMind API", + "builtin": false, + "example": "vaultmind-query" + }, + "vaultmind-status": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "vaultmind-status — Check VaultMind session status", + "builtin": false, + "example": "vaultmind-status" + }, + "version": { + "module": "module-system", + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "category": "system", + "desc": "version — Show version, build date, and cache key", + "builtin": false, + "example": "version" + }, + "vfs": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "vfs — command from terminal", + "builtin": true, + "example": "vfs" + }, + "vfs-clean": { + "module": "terminal", + "file": "modules/core/terminal.js", + "tier": "core-bundle", + "category": "system", + "desc": "vfs-clean — command from terminal", + "builtin": true, + "example": "vfs-clean" + }, + "vfs-export": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "vfs-export — Export the entire virtual filesystem as JSON", + "builtin": false, + "example": "vfs-export" + }, + "vfs-import": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "vfs-import — Import a previously exported VFS JSON file", + "builtin": false, + "example": "vfs-import" + }, + "vfs-size": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "vfs-size — command from module-fs", + "builtin": false, + "example": "vfs-size" + }, + "videos": { + "module": "module-searxng", + "file": "modules/core/module-searxng.js", + "tier": "core-bundle", + "category": "web", + "desc": "videos <query> — Search for videos", + "builtin": false, + "example": "videos" + }, + "voice": { + "module": "voice", + "file": "modules/ext/voice.js", + "tier": "ext-auto", + "category": "ui", + "desc": "voice [stop] — Start voice recording (Whisper STT), or type \"voice stop\" to cancel", + "builtin": false, + "example": "voice" + }, + "wallet-lookup": { + "module": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "category": "ai", + "desc": "wallet-lookup <address> — Look up Hyperliquid wallet summary", + "builtin": false, + "example": "wallet-lookup" + }, + "wc": { + "module": "module-fs", + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "category": "filesystem", + "desc": "wc <file> — Count lines, words, and bytes", + "builtin": false, + "example": "wc" + }, + "webdav-backup": { + "module": "webdav", + "file": "modules/ext/webdav.js", + "tier": "ext-auto", + "category": "cloud", + "desc": "Backup entire VFS to WebDAV as a dated JSON snapshot", + "builtin": false, + "example": "webdav-backup" + }, + "webdav-backup-list": { + "module": "webdav", + "file": "modules/ext/webdav.js", + "tier": "ext-auto", + "category": "cloud", + "desc": "List available backup files on WebDAV", + "builtin": false, + "example": "webdav-backup-list" + }, + "webdav-browser": { + "module": "webdav", + "file": "modules/ext/webdav.js", + "tier": "ext-auto", + "category": "cloud", + "desc": "webdav-browser [path] — Modern WebDAV file-tree browser with bulk operations", + "builtin": false, + "example": "webdav-browser" + }, + "webdav-config": { + "module": "webdav", + "file": "modules/ext/webdav.js", + "tier": "ext-auto", + "category": "cloud", + "desc": "webdav-config [url user pass] — Configure WebDAV. No args = auto-detect from current page URL", + "builtin": false, + "example": "webdav-config" + }, + "webdav-login": { + "module": "webdav", + "file": "modules/ext/webdav.js", + "tier": "ext-auto", + "category": "cloud", + "desc": "webdav-login [username] [password] — Open the login panel (Telegram or password) in THIS browser and configure WebDAV", + "builtin": false, + "example": "webdav-login" + }, + "webdav-ls": { + "module": "webdav", + "file": "modules/ext/webdav.js", + "tier": "ext-auto", + "category": "cloud", + "desc": "List files/directories on the WebDAV remote", + "builtin": false, + "example": "webdav-ls" + }, + "webdav-mkdir": { + "module": "webdav", + "file": "modules/ext/webdav.js", + "tier": "ext-auto", + "category": "cloud", + "desc": "webdav-mkdir <path> — Create a remote directory", + "builtin": false, + "example": "webdav-mkdir" + }, + "webdav-move": { + "module": "webdav", + "file": "modules/ext/webdav.js", + "tier": "ext-auto", + "category": "cloud", + "desc": "webdav-move <old> <new> — Alias for webdav-rename", + "builtin": false, + "example": "webdav-move" + }, + "webdav-pull": { + "module": "webdav", + "file": "modules/ext/webdav.js", + "tier": "ext-auto", + "category": "cloud", + "desc": "Download a WebDAV file to VFS", + "builtin": false, + "example": "webdav-pull" + }, + "webdav-push": { + "module": "webdav", + "file": "modules/ext/webdav.js", + "tier": "ext-auto", + "category": "cloud", + "desc": "Upload a VFS file to WebDAV", + "builtin": false, + "example": "webdav-push" + }, + "webdav-rename": { + "module": "webdav", + "file": "modules/ext/webdav.js", + "tier": "ext-auto", + "category": "cloud", + "desc": "webdav-rename <old> <new> — Rename or move a remote file/dir", + "builtin": false, + "example": "webdav-rename" + }, + "webdav-restore": { + "module": "webdav", + "file": "modules/ext/webdav.js", + "tier": "ext-auto", + "category": "cloud", + "desc": "Restore VFS from a WebDAV backup file", + "builtin": false, + "example": "webdav-restore" + }, + "webdav-rm": { + "module": "webdav", + "file": "modules/ext/webdav.js", + "tier": "ext-auto", + "category": "cloud", + "desc": "webdav-rm <path> — Delete a remote file or directory", + "builtin": false, + "example": "webdav-rm" + }, + "webdav-status": { + "module": "webdav", + "file": "modules/ext/webdav.js", + "tier": "ext-auto", + "category": "cloud", + "desc": "Show current WebDAV configuration status", + "builtin": false, + "example": "webdav-status" + }, + "webx": { + "module": "module-aiasm", + "file": "modules/core/module-aiasm.js", + "tier": "core-bundle", + "category": "general", + "desc": "webx <question> — Alias for webxai", + "builtin": false, + "example": "webx" + }, + "webxai": { + "module": "module-aiasm", + "file": "modules/core/module-aiasm.js", + "tier": "core-bundle", + "category": "general", + "desc": "webxai <question> — Send a question to the AI assistant", + "builtin": false, + "example": "webxai" + }, + "wget": { + "module": "cryptogzip", + "file": "modules/ext/cryptogzip.js", + "tier": "ext-auto", + "category": "system", + "desc": "wget <url> [filename] — Download a URL and save to VFS", + "builtin": false, + "example": "wget" + }, + "which": { + "module": "busybox", + "file": "modules/ext/busybox.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "which <name> — Locate a command in the module registry", + "builtin": false, + "example": "which" + }, + "whoami": { + "module": "busybox", + "file": "modules/ext/busybox.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "whoami — Show current user name", + "builtin": false, + "example": "whoami" + }, + "world-model": { + "module": "module-world-model", + "file": "modules/core/module-world-model.js", + "tier": "core-bundle", + "category": "ai", + "desc": "world-model — Show full world model as structured output", + "builtin": false, + "example": "world-model" + }, + "world-model-ask": { + "module": "module-world-model", + "file": "modules/core/module-world-model.js", + "tier": "core-bundle", + "category": "ai", + "desc": "world-model-ask <question> — Query the model (e.g. \"what changed since yesterday\")", + "builtin": false, + "example": "world-model-ask" + }, + "world-model-config": { + "module": "module-world-model", + "file": "modules/core/module-world-model.js", + "tier": "core-bundle", + "category": "ai", + "desc": "world-model-config <key> <value> — Tune: refresh-interval, max-projects", + "builtin": false, + "example": "world-model-config" + }, + "world-model-refresh": { + "module": "module-world-model", + "file": "modules/core/module-world-model.js", + "tier": "core-bundle", + "category": "ai", + "desc": "world-model-refresh — Force re-synthesis from all subsystems", + "builtin": false, + "example": "world-model-refresh" + }, + "world-model-summary": { + "module": "module-world-model", + "file": "modules/core/module-world-model.js", + "tier": "core-bundle", + "category": "ai", + "desc": "world-model-summary — One-line system self-awareness statement", + "builtin": false, + "example": "world-model-summary" + }, + "wpm": { + "module": "wpm", + "file": "modules/ext/wpm.js", + "tier": "ext-auto", + "category": "system", + "desc": "wpm <install|list|search|update|remove|info> [...] — Web Package Manager for installing and managing modules", + "builtin": false, + "example": "wpm" + }, + "wx": { + "module": "module-aiasm", + "file": "modules/core/module-aiasm.js", + "tier": "core-bundle", + "category": "general", + "desc": "wx <question> — Alias for webxai", + "builtin": false, + "example": "wx" + }, + "yes": { + "module": "busybox", + "file": "modules/ext/busybox.js", + "tier": "critical-ext-bundle", + "category": "shell", + "desc": "yes [string] — Output repeated string (default \"y\", capped at 50)", + "builtin": false, + "example": "yes" + } + }, + "modules": { + "alias": { + "file": "modules/ext/alias.js", + "tier": "critical-ext-bundle", + "commands": [ + "alias", + "unalias" + ], + "lines": 119 + }, + "busybox": { + "file": "modules/ext/busybox.js", + "tier": "critical-ext-bundle", + "commands": [ + "base64", + "basename", + "cal", + "cut", + "date", + "dirname", + "env", + "expand", + "hostname", + "printf", + "seq", + "sleep", + "uniq", + "which", + "whoami", + "yes" + ], + "lines": 415 + }, + "calc": { + "file": "modules/ext/calc.js", + "tier": "ext-auto", + "commands": [ + "calc" + ], + "lines": 107 + }, + "console": { + "file": "modules/ext/console.js", + "tier": "ext-auto", + "commands": [ + "js", + "js-run", + "node" + ], + "lines": 96 + }, + "cron": { + "file": "modules/ext/cron.js", + "tier": "ext-auto", + "commands": [ + "cron-clear", + "cron-every", + "cron-list", + "cron-stop" + ], + "lines": 111 + }, + "cryptogzip": { + "file": "modules/ext/cryptogzip.js", + "tier": "ext-auto", + "commands": [ + "decrypt", + "encrypt", + "gunzip", + "gzip", + "tar", + "untar", + "wget" + ], + "lines": 599 + }, + "debug-panel": { + "file": "modules/ext/debug-panel.js", + "tier": "ext-auto", + "commands": [ + "debug" + ], + "lines": 167 + }, + "diff": { + "file": "modules/ext/diff.js", + "tier": "critical-ext-bundle", + "commands": [ + "diff" + ], + "lines": 288 + }, + "editor": { + "file": "modules/ext/editor.js", + "tier": "critical-ext-bundle", + "commands": [ + "edit" + ], + "lines": 133 + }, + "filemgr": { + "file": "modules/ext/filemgr.js", + "tier": "critical-ext-bundle", + "commands": [ + "files", + "preview" + ], + "lines": 987 + }, + "finder": { + "file": "modules/ext/finder.js", + "tier": "critical-ext-bundle", + "commands": [ + "find" + ], + "lines": 48 + }, + "floating": { + "file": "modules/ext/floating.js", + "tier": "ext-auto", + "commands": [ + "floating-add", + "floating-clear", + "floating-list", + "floating-pos", + "floating-remove", + "floating-toggle" + ], + "lines": 444 + }, + "kanban": { + "file": "modules/ext/kanban.js", + "tier": "ext-auto", + "commands": [ + "kanban" + ], + "lines": 191 + }, + "media": { + "file": "modules/ext/media.js", + "tier": "ext-auto", + "commands": [ + "analyze" + ], + "lines": 508 + }, + "mindmap": { + "file": "modules/ext/mindmap.js", + "tier": "ext-auto", + "commands": [ + "mindmap" + ], + "lines": 1191 + }, + "module-agent-registry": { + "file": "modules/core/module-agent-registry.js", + "tier": "core-bundle", + "commands": [ + "a2a-ack", + "a2a-poll", + "a2a-post", + "a2a-prune", + "a2a-reject", + "a2a-status", + "agent-list", + "agent-register", + "agent-status", + "agent-unregister", + "contract-list", + "contract-register" + ], + "lines": 474 + }, + "module-aiasm": { + "file": "modules/core/module-aiasm.js", + "tier": "core-bundle", + "commands": [ + "webx", + "webxai", + "wx" + ], + "lines": 27 + }, + "module-aiassist": { + "file": "modules/core/module-aiassist.js", + "tier": "core-bundle", + "commands": [ + "agent-loops", + "ai", + "ai-allow", + "ai-clear", + "ai-compress", + "ai-config", + "ai-context", + "ai-deny", + "ai-ignore", + "ai-model", + "ai-stop", + "ai-system", + "archive-list", + "archive-read", + "ask", + "bench", + "bench-compare", + "commit-approve", + "commit-deny", + "commit-status", + "debug", + "debug-ai", + "debug-ai-last", + "debug-ai-log", + "debug-ai-send", + "guardrail", + "guardrail-add", + "guardrail-remove", + "mem-debug", + "reset-mem", + "shadow", + "shadow-commit", + "shadow-rollback", + "vaultmind-connect", + "vaultmind-query", + "vaultmind-status", + "wallet-lookup" + ], + "lines": 4376 + }, + "module-chatstudio": { + "file": "modules/core/module-chatstudio.js", + "tier": "core-bundle", + "commands": [ + "chat" + ], + "lines": 1138 + }, + "module-config": { + "file": "modules/core/module-config.js", + "tier": "core-bundle", + "commands": [ + "config", + "config-delete", + "config-export", + "config-list", + "config-reset", + "config-set" + ], + "lines": 93 + }, + "module-debug-supervisor": { + "file": "modules/core/module-debug-supervisor.js", + "tier": "core-bundle", + "commands": [ + "debug-analyze", + "debug-fix", + "debug-health", + "debug-history", + "debug-stats", + "debug-sync", + "debug-sync-url", + "debug-trends" + ], + "lines": 835 + }, + "module-fetcher": { + "file": "modules/core/module-fetcher.js", + "tier": "core-bundle", + "commands": [ + "extract", + "fetch" + ], + "lines": 116 + }, + "module-fs": { + "file": "modules/core/module-fs.js", + "tier": "core-bundle", + "commands": [ + "cat", + "cd", + "cp", + "download", + "du", + "echo", + "empty-trash", + "find", + "grep", + "head", + "ls", + "ls-trash", + "mkdir", + "mv", + "put", + "pwd", + "rm", + "rmdir", + "sed", + "session-delete", + "session-export", + "session-import", + "session-list", + "session-load", + "session-new", + "session-save", + "sort", + "tail", + "touch", + "trash", + "trash-dir", + "tree", + "upload", + "vfs-export", + "vfs-import", + "vfs-size", + "wc" + ], + "lines": 750 + }, + "module-leases": { + "file": "modules/core/module-leases.js", + "tier": "core-bundle", + "commands": [ + "lease-create", + "lease-list", + "lease-renew", + "lease-revoke" + ], + "lines": 138 + }, + "module-memory-graph": { + "file": "modules/core/module-memory-graph.js", + "tier": "core-bundle", + "commands": [ + "memgraph-agent", + "memgraph-decay-config", + "memgraph-export", + "memgraph-learn", + "memgraph-prune", + "memgraph-search", + "memgraph-session", + "memgraph-stats", + "memgraph-status" + ], + "lines": 663 + }, + "module-modmgr": { + "file": "modules/core/module-modmgr.js", + "tier": "core-bundle", + "commands": [ + "listmods", + "loadmod", + "module-disable", + "module-enable", + "module-info", + "module-list", + "module-load", + "module-reload", + "module-reset", + "module-stats", + "module-unload", + "modules", + "modules-list", + "removemod" + ], + "lines": 393 + }, + "module-neural-vfs": { + "file": "modules/core/module-neural-vfs.js", + "tier": "core-bundle", + "commands": [ + "nvfs-alias", + "nvfs-cancel", + "nvfs-config", + "nvfs-index", + "nvfs-index-graph", + "nvfs-resolve", + "nvfs-save", + "nvfs-status", + "nvfs-sync", + "nvfs-tree" + ], + "lines": 1108 + }, + "module-ollama": { + "file": "modules/core/module-ollama.js", + "tier": "core-bundle", + "commands": [ + "ai", + "ask", + "model-delete", + "model-info", + "model-pull", + "model-run", + "models", + "ollama", + "ollamaconnect", + "ollamadisconnect", + "ollamamodels", + "ollamasetmodel", + "ollamasetproxy" + ], + "lines": 130 + }, + "module-orchestrator": { + "file": "modules/core/module-orchestrator.js", + "tier": "core-bundle", + "commands": [ + "a2a-list", + "a2a-send", + "consensus", + "consensus-history", + "consensus-status", + "orchestrate", + "orchestrate-decompose", + "orchestrate-parallel", + "orchestrate-result", + "orchestrator-config", + "orchestrator-kill", + "orchestrator-list", + "orchestrator-purge", + "orchestrator-restart-policy", + "orchestrator-schedule", + "orchestrator-schedules", + "orchestrator-sleep", + "orchestrator-spawn", + "orchestrator-status", + "orchestrator-unschedule", + "orchestrator-wake", + "pipeline" + ], + "lines": 1380 + }, + "module-predictor": { + "file": "modules/core/module-predictor.js", + "tier": "core-bundle", + "commands": [ + "predict", + "predictor-config", + "predictor-status" + ], + "lines": 282 + }, + "module-sandbox": { + "file": "modules/core/module-sandbox.js", + "tier": "core-bundle", + "commands": [ + "sandbox-config", + "sandbox-run", + "sandbox-status" + ], + "lines": 372 + }, + "module-searxng": { + "file": "modules/core/module-searxng.js", + "tier": "core-bundle", + "commands": [ + "images", + "s", + "search", + "videos" + ], + "lines": 116 + }, + "module-servermon": { + "file": "modules/core/module-servermon.js", + "tier": "core-bundle", + "commands": [ + "monitor", + "server", + "server-check", + "server-remove", + "servers", + "system-health", + "test-net" + ], + "lines": 252 + }, + "module-simplegui": { + "file": "modules/core/module-simplegui.js", + "tier": "core-bundle", + "commands": [ + "gui", + "gui-button", + "gui-clear", + "gui-input", + "gui-run", + "gui-text" + ], + "lines": 78 + }, + "module-smartctx": { + "file": "modules/core/module-smartctx.js", + "tier": "core-bundle", + "commands": [ + "context", + "context-auto", + "context-delete", + "context-new", + "contexts", + "smart-ask" + ], + "lines": 285 + }, + "module-system": { + "file": "modules/core/module-system.js", + "tier": "core-bundle", + "commands": [ + "bug-report", + "checkpoint", + "checkpoint-del", + "checkpoint-list", + "doctor", + "echo", + "errors", + "factory-reset", + "factory-reset-old", + "font-size", + "health-check", + "help", + "logrotate", + "logs", + "mem", + "memory-map-export", + "preview", + "reset", + "reset-vfs", + "rollback", + "setfont", + "status", + "uptime", + "version" + ], + "lines": 762 + }, + "module-world-model": { + "file": "modules/core/module-world-model.js", + "tier": "core-bundle", + "commands": [ + "world-model", + "world-model-ask", + "world-model-config", + "world-model-refresh", + "world-model-summary" + ], + "lines": 502 + }, + "secret-vault": { + "file": "modules/core/secret-vault.js", + "tier": "core-bundle", + "commands": [ + "secret-del", + "secret-get", + "secret-list", + "secret-lock", + "secret-set", + "secret-setpin", + "secret-status", + "secret-unlock" + ], + "lines": 442 + }, + "shell": { + "file": "modules/ext/shell.js", + "tier": "critical-ext-bundle", + "commands": [ + "export", + "script", + "set", + "sh", + "unset" + ], + "lines": 627 + }, + "skill-pack-core": { + "file": "modules/ext/skill-pack-core.js", + "tier": "ext-auto", + "commands": [ + "learn-skill", + "skill-audit", + "skill-create", + "skill-delete", + "skill-export", + "skill-import", + "skill-list", + "skill-pack-core", + "skill-patch", + "skill-restore", + "skill-sync", + "skill-validate", + "skill-view", + "skill-webdav-sync" + ], + "lines": 1150 + }, + "theme": { + "file": "modules/ext/theme.js", + "tier": "ext-auto", + "commands": [ + "theme" + ], + "lines": 173 + }, + "three": { + "file": "modules/ext/three.js", + "tier": "ext-auto", + "commands": [ + "three" + ], + "lines": 2426 + }, + "tutorial": { + "file": "modules/ext/tutorial.js", + "tier": "ext-auto", + "commands": [ + "tutorial" + ], + "lines": 283 + }, + "voice": { + "file": "modules/ext/voice.js", + "tier": "ext-auto", + "commands": [ + "speak", + "voice" + ], + "lines": 306 + }, + "webdav": { + "file": "modules/ext/webdav.js", + "tier": "ext-auto", + "commands": [ + "webdav-backup", + "webdav-backup-list", + "webdav-browser", + "webdav-config", + "webdav-login", + "webdav-ls", + "webdav-mkdir", + "webdav-move", + "webdav-pull", + "webdav-push", + "webdav-rename", + "webdav-restore", + "webdav-rm", + "webdav-status" + ], + "lines": 2346 + }, + "wpm": { + "file": "modules/ext/wpm.js", + "tier": "ext-auto", + "commands": [ + "wpm" + ], + "lines": 190 + } + }, + "coverage": { + "commands": 344, + "described": 344, + "modules": 46, + "tiers": { + "core-bundle": 257, + "critical-ext-bundle": 28, + "ext-auto": 59, + "ext-manual": 0 + } + } +}
\ No newline at end of file diff --git a/course/changelog.jsonl b/course/changelog.jsonl new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/course/changelog.jsonl diff --git a/course/chapters/01-orientation.json b/course/chapters/01-orientation.json new file mode 100644 index 0000000..165e140 --- /dev/null +++ b/course/chapters/01-orientation.json @@ -0,0 +1,132 @@ +{ + "id": "01-orientation", + "title": "Orientation — what newOS4 is", + "icon": "🚀", + "level": "beginner", + "lessons": [ + { + "id": "1.1", + "title": "A terminal in your browser", + "objective": "Understand what newOS4 is and run your first three commands.", + "minutes": 6, + "commands": [ + "help", + "status", + "ls" + ], + "blocks": [ + { + "type": "text", + "text": "newOS4 is a terminal-centric browser OS. Every command runs in your tab, files live in a virtual filesystem (VFS), and modules add commands at runtime." + }, + { + "type": "cmd", + "text": "help" + }, + { + "type": "text", + "text": "`help` lists every registered command grouped by the module that owns it." + }, + { + "type": "cmd", + "text": "status" + }, + { + "type": "cmd", + "text": "ls" + }, + { + "type": "tip", + "text": "The VFS survives reloads. Create a file with `touch notes.txt`, then run `ls`." + } + ], + "exercise": { + "instruction": "Run `status` and read the loaded-modules line.", + "accept": [ + "status" + ], + "check": { + "type": "command" + }, + "hint": "Type: status" + }, + "quiz": { + "q": "Where do files live in newOS4?", + "options": [ + "On the web server", + "In the browser VFS", + "In a SQL database" + ], + "answer": 1 + }, + "sources": [ + "modules/core/terminal.js", + "modules/core/virtual-fs.js" + ] + }, + { + "id": "1.2", + "title": "Finding your way around", + "objective": "Navigate the virtual filesystem and create your first file.", + "minutes": 8, + "commands": [ + "pwd", + "cd", + "tree", + "cat", + "touch" + ], + "blocks": [ + { + "type": "text", + "text": "The VFS is a tree rooted at `/`. `pwd` prints where you are, `cd` moves, `tree` shows the branch, `cat` prints a file." + }, + { + "type": "cmd", + "text": "pwd" + }, + { + "type": "cmd", + "text": "tree" + }, + { + "type": "warn", + "text": "`echo \"text\" > file` is NOT supported by the VFS. Use `touch`, the editor (`edit <file>`), or `webx11.fs.writeFile()` in the console." + }, + { + "type": "cmd", + "text": "touch hello.txt" + }, + { + "type": "cmd", + "text": "ls" + } + ], + "exercise": { + "instruction": "From the root, create `hello.txt` and list it.", + "accept": [ + "touch hello.txt", + "ls" + ], + "check": { + "type": "file", + "path": "/hello.txt" + }, + "hint": "Type: touch hello.txt — then ls" + }, + "quiz": { + "q": "Which command prints the current directory?", + "options": [ + "ls", + "pwd", + "cd" + ], + "answer": 1 + }, + "sources": [ + "modules/core/module-fs.js", + "modules/core/virtual-fs.js" + ] + } + ] +}
\ No newline at end of file diff --git a/course/chapters/02-architecture.json b/course/chapters/02-architecture.json new file mode 100644 index 0000000..7dee513 --- /dev/null +++ b/course/chapters/02-architecture.json @@ -0,0 +1,131 @@ +{ + "id": "02-architecture", + "title": "Architecture without the mystery", + "icon": "🏗", + "level": "beginner", + "lessons": [ + { + "id": "2.1", + "title": "Terminal, modules, commands", + "objective": "See how the terminal, module manager and command registry fit together.", + "minutes": 9, + "commands": [ + "modules", + "status", + "help" + ], + "blocks": [ + { + "type": "text", + "text": "`terminal.js` parses input and dispatches a command to the module that registered it. Each module owns a `commands` map and contributes help strings." + }, + { + "type": "cmd", + "text": "modules" + }, + { + "type": "text", + "text": "The module manager lists every loaded module and its command count." + }, + { + "type": "cmd", + "text": "status" + }, + { + "type": "text", + "text": "`status` shows files, modules and network state in one place." + }, + { + "type": "cmd", + "text": "check" + }, + { + "type": "tip", + "text": "A command that exists in `help` but fails at runtime usually has an unmet module requirement that `check` reports." + } + ], + "exercise": { + "instruction": "List the loaded modules.", + "accept": [ + "modules", + "status" + ], + "check": { + "type": "command" + }, + "hint": "Type: modules" + }, + "quiz": { + "q": "Who executes a command after you press Enter?", + "options": [ + "nginx", + "the module that registered it", + "the browser cache" + ], + "answer": 1 + }, + "sources": [ + "modules/core/terminal.js", + "modules/core/module-manager.js", + "modules/core/module-system.js" + ] + }, + { + "id": "2.2", + "title": "VFS and the three loading tiers", + "objective": "Understand where files live and why a module edit sometimes needs a rebuild and sometimes does not.", + "minutes": 10, + "commands": [ + "status", + "help", + "load-local" + ], + "blocks": [ + { + "type": "text", + "text": "Files live in `virtual-fs.js` (in-memory + localStorage persistence). Directories are nodes; `readFile()` returns `null` for a missing path." + }, + { + "type": "text", + "text": "Modules ship in three tiers:" + }, + { + "type": "table", + "text": "core-bundle.js — core modules (requires rebuild) | critical-ext-bundle.js — editor, shell, alias, finder, filemgr, diff, busybox (requires rebuild) | individually loaded ext modules — cache-busted by LATEST_VER (no rebuild)" + }, + { + "type": "cmd", + "text": "status" + }, + { + "type": "warn", + "text": "Individually loaded modules are cached with `?v=<webx11_cache_ver>`. Without bumping `LATEST_VER`, users keep the old file." + } + ], + "exercise": { + "instruction": "Run `status` and find the VFS size line.", + "accept": [ + "status" + ], + "check": { + "type": "command" + }, + "hint": "Type: status" + }, + "quiz": { + "q": "Which tier needs `rebuild-bundles.py` after an edit?", + "options": [ + "core-bundle.js", + "webdav.js", + "course.js" + ], + "answer": 0 + }, + "sources": [ + "modules/core/virtual-fs.js", + "modules/core/module-loader.js", + "scripts/rebuild-bundles.py" + ] + } + ] +}
\ No newline at end of file diff --git a/course/chapters/03-commands.json b/course/chapters/03-commands.json new file mode 100644 index 0000000..10807cb --- /dev/null +++ b/course/chapters/03-commands.json @@ -0,0 +1,119 @@ +{ + "id": "03-commands", + "title": "Command mastery", + "icon": "⌨", + "level": "beginner", + "lessons": [ + { + "id": "3.1", + "title": "Find any command fast", + "objective": "Use help, commands and check to discover what exists before guessing.", + "minutes": 7, + "commands": [ + "help", + "status" + ], + "blocks": [ + { + "type": "text", + "text": "`help` groups all 344 commands by the module that owns them." + }, + { + "type": "cmd", + "text": "help" + }, + { + "type": "cmd", + "text": "help webdav-push" + }, + { + "type": "tip", + "text": "`check` shows module requirements — the fastest way to explain a command that is listed but not working." + } + ], + "exercise": { + "instruction": "Open the help for `webdav-push`.", + "accept": [ + "help webdav-push" + ], + "check": { + "type": "command" + }, + "hint": "Type: help webdav-push" + }, + "quiz": { + "q": "Which command shows usage for one command?", + "options": [ + "help <cmd>", + "status", + "ls" + ], + "answer": 0 + }, + "sources": [ + "modules/core/module-system.js", + "modules/core/module-manager.js" + ] + }, + { + "id": "3.2", + "title": "Pipes, grep, sed, tail", + "objective": "Combine commands and understand the two semantics that bite: per-line sed and tail -f deltas.", + "minutes": 12, + "commands": [ + "grep", + "sed", + "tail", + "sort", + "head" + ], + "blocks": [ + { + "type": "text", + "text": "Pipes feed one command into the next. `grep` filters lines, `sed` substitutes text, `tail` prints the end of a file." + }, + { + "type": "cmd", + "text": "ls | grep txt" + }, + { + "type": "warn", + "text": "`sed s/x/y/` substitutes the first match per LINE, not per file. `sed -i` is flagged destructive and needs confirmation." + }, + { + "type": "text", + "text": "`tail -f` is interactive and stops with Ctrl+C. AI-initiated commands get a bounded read instead." + }, + { + "type": "cmd", + "text": "tail hello.txt" + } + ], + "exercise": { + "instruction": "Search the current listing for `txt`.", + "accept": [ + "ls | grep txt", + "grep txt" + ], + "check": { + "type": "command" + }, + "hint": "Type: ls | grep txt" + }, + "quiz": { + "q": "`sed s/a/b/` replaces…", + "options": [ + "every match in the file", + "the first match on each line", + "only the last line" + ], + "answer": 1 + }, + "sources": [ + "modules/core/module-fs.js", + "modules/ext/shell.js", + "modules/core/terminal.js" + ] + } + ] +}
\ No newline at end of file diff --git a/course/chapters/04-ai.json b/course/chapters/04-ai.json new file mode 100644 index 0000000..3552c53 --- /dev/null +++ b/course/chapters/04-ai.json @@ -0,0 +1,128 @@ +{ + "id": "04-ai", + "title": "The AI assistant", + "icon": "🤖", + "level": "intermediate", + "lessons": [ + { + "id": "4.1", + "title": "Ask, and let it use tools", + "objective": "Use the AI assistant and understand the confirmation boundary.", + "minutes": 9, + "commands": [ + "ai", + "ask", + "ai-config", + "ai-model" + ], + "blocks": [ + { + "type": "text", + "text": "`ai` and `ask` open the tool-calling assistant. It can read files, run safe commands and search the web." + }, + { + "type": "cmd", + "text": "ai what can you do?" + }, + { + "type": "text", + "text": "Destructive actions (writes, `sed -i`, deletes) require confirmation. AI-initiated commands run with a loop guard so they cannot recurse." + }, + { + "type": "cmd", + "text": "ai-model" + }, + { + "type": "cmd", + "text": "ai-config" + } + ], + "exercise": { + "instruction": "Ask the AI a short question.", + "accept": [ + "ai *", + "ask *" + ], + "check": { + "type": "command" + }, + "hint": "Type: ai \"hello\"" + }, + "quiz": { + "q": "What happens when the AI wants a destructive command?", + "options": [ + "runs silently", + "asks for confirmation", + "restarts the terminal" + ], + "answer": 1 + }, + "sources": [ + "modules/core/module-aiassist.js", + "modules/core/ollama-manager.js" + ] + }, + { + "id": "4.2", + "title": "Context, memory and skills", + "objective": "Inspect and clear context, then browse the skill library the AI retrieves from.", + "minutes": 8, + "commands": [ + "ai-context", + "ai-clear", + "skill-list", + "skill-validate", + "learn-skill" + ], + "blocks": [ + { + "type": "text", + "text": "The assistant keeps recent context. `ai-context` shows it; `ai-clear` resets it without touching your files." + }, + { + "type": "cmd", + "text": "ai-context" + }, + { + "type": "cmd", + "text": "skill-list" + }, + { + "type": "text", + "text": "Skills are markdown entries in `/skills/` indexed by `/skills/index.json`; `load_skills` retrieves them by query." + }, + { + "type": "cmd", + "text": "skill-validate course-01-orientation" + }, + { + "type": "tip", + "text": "This course installs its chapters as `course-*` skills, so the AI can teach what you are reading." + } + ], + "exercise": { + "instruction": "List the installed skills.", + "accept": [ + "skill-list" + ], + "check": { + "type": "command" + }, + "hint": "Type: skill-list" + }, + "quiz": { + "q": "Where does the skill index live?", + "options": [ + "/skills/index.json", + "/etc/skills", + "localStorage only" + ], + "answer": 0 + }, + "sources": [ + "modules/ext/skill-pack-core.js", + "modules/core/module-aiassist.js" + ] + } + ] +}
\ No newline at end of file diff --git a/course/chapters/05-modules.json b/course/chapters/05-modules.json new file mode 100644 index 0000000..4b68f42 --- /dev/null +++ b/course/chapters/05-modules.json @@ -0,0 +1,115 @@ +{ + "id": "05-modules", + "title": "Modules & packages", + "icon": "🧩", + "level": "intermediate", + "lessons": [ + { + "id": "5.1", + "title": "Browse and install modules", + "objective": "See what is loaded and install from the WebX11 package index.", + "minutes": 8, + "commands": [ + "modules", + "wpm", + "load-local" + ], + "blocks": [ + { + "type": "text", + "text": "`wpm` is the package manager for terminal modules: list the index, install a module from `packages.json` or a URL." + }, + { + "type": "cmd", + "text": "wpm list" + }, + { + "type": "cmd", + "text": "modules" + }, + { + "type": "text", + "text": "Every ext module in the default build auto-loads. There is no separate “needs loadmod” tier." + }, + { + "type": "cmd", + "text": "load-local modules/ext/calc.js" + } + ], + "exercise": { + "instruction": "List available packages.", + "accept": [ + "wpm list" + ], + "check": { + "type": "command" + }, + "hint": "Type: wpm list" + }, + "quiz": { + "q": "What does wpm install?", + "options": [ + "terminal modules", + "system packages", + "browser extensions" + ], + "answer": 0 + }, + "sources": [ + "modules/ext/wpm.js", + "packages.json", + "modules/core/module-loader.js" + ] + }, + { + "id": "5.2", + "title": "Module anatomy (a complete mini module)", + "objective": "Read the contract every module follows: class, commands map, help info, registration.", + "minutes": 12, + "commands": [ + "load-local" + ], + "blocks": [ + { + "type": "text", + "text": "A module is a class with `commands`, `registerCommands()`, `getHelpInfo()` and optionally `init(terminal)`. The module manager injects `terminal` before calling `registerCommands()`." + }, + { + "type": "code", + "text": "class HelloModule {\n constructor(){ this.commands = {}; }\n getHelpInfo(){ return { hello: 'hello — print a greeting' }; }\n registerCommands(){ this.commands['hello'] = () => this.terminal.print('hello newOS4'); }\n}" + }, + { + "type": "tip", + "text": "Tier-3 modules register at the end with `webx11.moduleManager.registerModule(new HelloModule(), url)`." + }, + { + "type": "cmd", + "text": "modules" + } + ], + "exercise": { + "instruction": "Open the module list and find a module you have not used.", + "accept": [ + "modules" + ], + "check": { + "type": "command" + }, + "hint": "Type: modules" + }, + "quiz": { + "q": "What does the module manager inject before registerCommands()?", + "options": [ + "the terminal", + "the database", + "nothing" + ], + "answer": 0 + }, + "sources": [ + "modules/core/module-manager.js", + "modules/ext/kanban.js" + ] + } + ] +}
\ No newline at end of file diff --git a/course/chapters/06-files-cloud.json b/course/chapters/06-files-cloud.json new file mode 100644 index 0000000..697a5f5 --- /dev/null +++ b/course/chapters/06-files-cloud.json @@ -0,0 +1,132 @@ +{ + "id": "06-files-cloud", + "title": "Files, editor, WebDAV", + "icon": "☁", + "level": "intermediate", + "lessons": [ + { + "id": "6.1", + "title": "Edit and compare files", + "objective": "Use the editor and diff tools on the VFS.", + "minutes": 9, + "commands": [ + "edit", + "diff", + "find", + "files" + ], + "blocks": [ + { + "type": "text", + "text": "`edit` opens the VFS editor. `diff` compares two files. `find` searches names; `filemgr` opens the visual browser." + }, + { + "type": "cmd", + "text": "edit hello.txt" + }, + { + "type": "cmd", + "text": "find hello" + }, + { + "type": "cmd", + "text": "diff hello.txt hello.txt" + }, + { + "type": "tip", + "text": "The editor calls the tutorial/file-saved hook, so exercise progress can be detected from a save." + }, + { + "type": "cmd", + "text": "files" + } + ], + "exercise": { + "instruction": "Search for your file by name.", + "accept": [ + "find hello", + "find hello.txt" + ], + "check": { + "type": "command" + }, + "hint": "Type: find hello" + }, + "quiz": { + "q": "Which command opens the visual file manager?", + "options": [ + "filemgr", + "ls", + "cat" + ], + "answer": 0 + }, + "sources": [ + "modules/ext/editor.js", + "modules/ext/diff.js", + "modules/ext/filemgr.js" + ] + }, + { + "id": "6.2", + "title": "Cloud sync and login", + "objective": "Understand WebDAV auth, the space token, and the 401 self-heal path.", + "minutes": 14, + "commands": [ + "webdav-login", + "webdav-config", + "webdav-status", + "webdav-push", + "webdav-backup" + ], + "blocks": [ + { + "type": "text", + "text": "WebDAV syncs your VFS to your space. The module holds a SPACE token, not your personal token; it refreshes on 401." + }, + { + "type": "cmd", + "text": "webdav-status" + }, + { + "type": "cmd", + "text": "webdav-login" + }, + { + "type": "warn", + "text": "Sessions do not cross browser contexts. If a push 401s, run `webdav-login` in THIS browser, then retry." + }, + { + "type": "cmd", + "text": "webdav-push" + }, + { + "type": "cmd", + "text": "webdav-backup" + } + ], + "exercise": { + "instruction": "Check the WebDAV status.", + "accept": [ + "webdav-status" + ], + "check": { + "type": "command" + }, + "hint": "Type: webdav-status" + }, + "quiz": { + "q": "A 401 push usually means…", + "options": [ + "the file is too big", + "no session/token in this browser context", + "WebDAV is disabled" + ], + "answer": 1 + }, + "sources": [ + "modules/ext/webdav.js" + ] + } + ] +}
\ No newline at end of file diff --git a/course/chapters/07-automation.json b/course/chapters/07-automation.json new file mode 100644 index 0000000..3345fc2 --- /dev/null +++ b/course/chapters/07-automation.json @@ -0,0 +1,134 @@ +{ + "id": "07-automation", + "title": "Automation", + "icon": "⚙", + "level": "advanced", + "lessons": [ + { + "id": "7.1", + "title": "Schedule commands with cron", + "objective": "Run a command on an interval and manage the schedule.", + "minutes": 8, + "commands": [ + "cron-every", + "cron-list", + "cron-stop", + "cron-clear" + ], + "blocks": [ + { + "type": "text", + "text": "`cron-every` schedules a command inside the terminal (minimum 5 seconds). Jobs live only while the tab is open." + }, + { + "type": "cmd", + "text": "cron-every 60 echo hello" + }, + { + "type": "cmd", + "text": "cron-list" + }, + { + "type": "cmd", + "text": "cron-stop 1" + }, + { + "type": "cmd", + "text": "cron-clear" + }, + { + "type": "tip", + "text": "For server-side schedules use Hermes cron; the terminal scheduler is per-tab." + } + ], + "exercise": { + "instruction": "List active cron jobs.", + "accept": [ + "cron-list" + ], + "check": { + "type": "command" + }, + "hint": "Type: cron-list" + }, + "quiz": { + "q": "How long does a terminal cron job live?", + "options": [ + "forever on the server", + "while the tab is open", + "until reboot" + ], + "answer": 1 + }, + "sources": [ + "modules/ext/cron.js" + ] + }, + { + "id": "7.2", + "title": "Kanban and checkpoints", + "objective": "Track work on a board and snapshot the VFS before risky changes.", + "minutes": 8, + "commands": [ + "kanban", + "checkpoint", + "checkpoint-list", + "rollback" + ], + "blocks": [ + { + "type": "text", + "text": "`kanban` is a local board with todo/doing/done columns. `checkpoint` snapshots the VFS; `rollback` restores it." + }, + { + "type": "cmd", + "text": "kanban add \"Try the course\"" + }, + { + "type": "cmd", + "text": "kanban list" + }, + { + "type": "cmd", + "text": "checkpoint before-project" + }, + { + "type": "cmd", + "text": "checkpoint-list" + }, + { + "type": "cmd", + "text": "rollback before-project" + }, + { + "type": "warn", + "text": "`rollback` replaces current VFS state — checkpoint before experiments." + } + ], + "exercise": { + "instruction": "Create a checkpoint.", + "accept": [ + "checkpoint *", + "checkpoint" + ], + "check": { + "type": "command" + }, + "hint": "Type: checkpoint my-test" + }, + "quiz": { + "q": "What does checkpoint do?", + "options": [ + "snapshots the VFS", + "deletes files", + "pushes to WebDAV" + ], + "answer": 0 + }, + "sources": [ + "modules/ext/kanban.js", + "modules/core/module-fs.js" + ] + } + ] +}
\ No newline at end of file diff --git a/course/chapters/08-build.json b/course/chapters/08-build.json new file mode 100644 index 0000000..48a60bd --- /dev/null +++ b/course/chapters/08-build.json @@ -0,0 +1,117 @@ +{ + "id": "08-build", + "title": "Build & extend", + "icon": "🔧", + "level": "advanced", + "lessons": [ + { + "id": "8.1", + "title": "Add a command safely", + "objective": "Follow the edit → syntax → test → rebuild → version-bump loop.", + "minutes": 12, + "commands": [ + "load-local", + "status", + "help" + ], + "blocks": [ + { + "type": "text", + "text": "Edit the source, run `node --check`, run the relevant test harness, rebuild bundles for tiers 1–2, then bump BOTH `main.html` and `LATEST_VER`." + }, + { + "type": "cmd", + "text": "status" + }, + { + "type": "cmd", + "text": "help load-local" + }, + { + "type": "warn", + "text": "Editing a tier-3 module without bumping `LATEST_VER` means users keep the stale cached file indefinitely." + } + ], + "exercise": { + "instruction": "Run the built-in test suite.", + "accept": [ + "status" + ], + "check": { + "type": "command" + }, + "hint": "Type: test auto" + }, + "quiz": { + "q": "Which two places carry the version?", + "options": [ + "main.html and LATEST_VER", + "package.json only", + "localStorage" + ], + "answer": 0 + }, + "sources": [ + "scripts/rebuild-bundles.py", + "modules/core/module-loader.js" + ] + }, + { + "id": "8.2", + "title": "Debug and observe", + "objective": "Use the debug panel, console and health checks when something breaks.", + "minutes": 8, + "commands": [ + "debug", + "health-check", + "js" + ], + "blocks": [ + { + "type": "text", + "text": "`debug` toggles the runtime debug panel; `console` opens a JS console in the terminal; `health-check` audits VFS, modules, logs and skills." + }, + { + "type": "cmd", + "text": "health-check" + }, + { + "type": "cmd", + "text": "debug" + }, + { + "type": "tip", + "text": "Errors are posted to `/spaces/newos4/api/debug/ingest.php` and clustered in the debug dashboard." + }, + { + "type": "cmd", + "text": "js 1+1" + } + ], + "exercise": { + "instruction": "Run a health check.", + "accept": [ + "health-check" + ], + "check": { + "type": "command" + }, + "hint": "Type: health-check" + }, + "quiz": { + "q": "Where do runtime errors get ingested?", + "options": [ + "/api/debug/ingest.php", + "/dev/null", + "localStorage" + ], + "answer": 0 + }, + "sources": [ + "modules/ext/debug-panel.js", + "modules/core/module-debug-supervisor.js", + "api/debug/ingest.php" + ] + } + ] +}
\ No newline at end of file diff --git a/course/course.json b/course/course.json new file mode 100644 index 0000000..a355156 --- /dev/null +++ b/course/course.json @@ -0,0 +1,156 @@ +{ + "version": "2026.10.18.1", + "updated": "2026-10-18", + "chapters": [ + { + "id": "01-orientation", + "title": "Orientation — what newOS4 is", + "icon": "🚀", + "level": "beginner", + "lessons": [ + { + "id": "1.1", + "title": "A terminal in your browser", + "minutes": 6 + }, + { + "id": "1.2", + "title": "Finding your way around", + "minutes": 8 + } + ] + }, + { + "id": "02-architecture", + "title": "Architecture without the mystery", + "icon": "🏗", + "level": "beginner", + "lessons": [ + { + "id": "2.1", + "title": "Terminal, modules, commands", + "minutes": 9 + }, + { + "id": "2.2", + "title": "VFS and the three loading tiers", + "minutes": 10 + } + ] + }, + { + "id": "03-commands", + "title": "Command mastery", + "icon": "⌨", + "level": "beginner", + "lessons": [ + { + "id": "3.1", + "title": "Find any command fast", + "minutes": 7 + }, + { + "id": "3.2", + "title": "Pipes, grep, sed, tail", + "minutes": 12 + } + ] + }, + { + "id": "04-ai", + "title": "The AI assistant", + "icon": "🤖", + "level": "intermediate", + "lessons": [ + { + "id": "4.1", + "title": "Ask, and let it use tools", + "minutes": 9 + }, + { + "id": "4.2", + "title": "Context, memory and skills", + "minutes": 8 + } + ] + }, + { + "id": "05-modules", + "title": "Modules & packages", + "icon": "🧩", + "level": "intermediate", + "lessons": [ + { + "id": "5.1", + "title": "Browse and install modules", + "minutes": 8 + }, + { + "id": "5.2", + "title": "Module anatomy (a complete mini module)", + "minutes": 12 + } + ] + }, + { + "id": "06-files-cloud", + "title": "Files, editor, WebDAV", + "icon": "☁", + "level": "intermediate", + "lessons": [ + { + "id": "6.1", + "title": "Edit and compare files", + "minutes": 9 + }, + { + "id": "6.2", + "title": "Cloud sync and login", + "minutes": 14 + } + ] + }, + { + "id": "07-automation", + "title": "Automation", + "icon": "⚙", + "level": "advanced", + "lessons": [ + { + "id": "7.1", + "title": "Schedule commands with cron", + "minutes": 8 + }, + { + "id": "7.2", + "title": "Kanban and checkpoints", + "minutes": 8 + } + ] + }, + { + "id": "08-build", + "title": "Build & extend", + "icon": "🔧", + "level": "advanced", + "lessons": [ + { + "id": "8.1", + "title": "Add a command safely", + "minutes": 12 + }, + { + "id": "8.2", + "title": "Debug and observe", + "minutes": 8 + } + ] + } + ], + "projects": [ + "notes-bot", + "daily-digest", + "sync-backup" + ], + "feed": [] +}
\ No newline at end of file diff --git a/course/faq.json b/course/faq.json new file mode 100644 index 0000000..fc69ce2 --- /dev/null +++ b/course/faq.json @@ -0,0 +1,3 @@ +{ + "items": [] +}
\ No newline at end of file diff --git a/course/feed.json b/course/feed.json new file mode 100644 index 0000000..81aac99 --- /dev/null +++ b/course/feed.json @@ -0,0 +1,4 @@ +{ + "version": "2026-10-18", + "items": [] +}
\ No newline at end of file diff --git a/course/projects/daily-digest.json b/course/projects/daily-digest.json new file mode 100644 index 0000000..e78ad46 --- /dev/null +++ b/course/projects/daily-digest.json @@ -0,0 +1,38 @@ +{ + "id": "daily-digest", + "title": "Automated daily digest", + "level": "intermediate", + "minutes": 25, + "goal": "Schedule a repeating web fetch, save the result to a file, and inspect it.", + "lesson": "7.1", + "steps": [ + { + "instruction": "Fetch a page and confirm output", + "command": "fetch https://example.com", + "check": { + "type": "command" + } + }, + { + "instruction": "Schedule a 60s echo so you can see cron work", + "command": "cron-every 60 echo digest", + "check": { + "type": "command" + } + }, + { + "instruction": "List the schedule", + "command": "cron-list", + "check": { + "type": "command" + } + }, + { + "instruction": "Stop the job after one run", + "command": "cron-stop 1", + "check": { + "type": "command" + } + } + ] +}
\ No newline at end of file diff --git a/course/projects/notes-bot.json b/course/projects/notes-bot.json new file mode 100644 index 0000000..53f8230 --- /dev/null +++ b/course/projects/notes-bot.json @@ -0,0 +1,39 @@ +{ + "id": "notes-bot", + "title": "Personal notes with AI summaries", + "level": "beginner", + "minutes": 20, + "goal": "Create a notes folder, write a note, ask the AI to summarize it, and export it.", + "lesson": "1.2", + "steps": [ + { + "instruction": "Create the notes folder", + "command": "mkdir notes", + "check": { + "type": "command" + } + }, + { + "instruction": "Create a note file", + "command": "touch notes/today.txt", + "check": { + "type": "file", + "path": "/notes/today.txt" + } + }, + { + "instruction": "Open it in the editor and write 3 lines", + "command": "edit notes/today.txt", + "check": { + "type": "manual" + } + }, + { + "instruction": "Ask the AI to summarize the note", + "command": "ai summarize /notes/today.txt", + "check": { + "type": "manual" + } + } + ] +}
\ No newline at end of file diff --git a/course/projects/sync-backup.json b/course/projects/sync-backup.json new file mode 100644 index 0000000..20c950d --- /dev/null +++ b/course/projects/sync-backup.json @@ -0,0 +1,38 @@ +{ + "id": "sync-backup", + "title": "Cloud backup with WebDAV", + "level": "intermediate", + "minutes": 30, + "goal": "Log in, check the space, push the VFS, and create a restorable backup.", + "lesson": "6.2", + "steps": [ + { + "instruction": "Check the WebDAV state", + "command": "webdav-status", + "check": { + "type": "command" + } + }, + { + "instruction": "Log in inside THIS browser if a push 401s", + "command": "webdav-login", + "check": { + "type": "manual" + } + }, + { + "instruction": "Push the VFS", + "command": "webdav-push", + "check": { + "type": "manual" + } + }, + { + "instruction": "Create a backup", + "command": "webdav-backup", + "check": { + "type": "command" + } + } + ] +}
\ No newline at end of file @@ -1234,6 +1234,10 @@ style="border-color:var(--success-color);color:var(--success-color);padding:12px 28px;font-size:1em;"> 🎮 Start Tutorial </button> + <button class="btn" onclick="var m=document.getElementById('welcome-modal');if(m)m.style.display='none';location.href='course.html'" + style="border-color:var(--info-color);color:var(--info-color);padding:12px 28px;font-size:1em;"> + 📚 Full Course + </button> <button class="btn" onclick="if(webx11)webx11.dismissWelcome()" style="border-color:var(--text-muted);color:var(--text-muted);padding:12px 28px;font-size:1em;"> Skip — I'll explore @@ -1308,7 +1312,7 @@ var mods = [ ['Foundation (always loaded)', ['virtual-fs.js','module-manager.js','ollama-manager.js','terminal.js'], true], ['Built-in modules', ['module-system.js','module-fs.js','module-ollama.js','module-my.js','module-searxng.js','module-servermon.js','module-ollamamodels.js','module-config.js','module-simplegui.js','module-io.js','module-dynamicprompts.js','module-aiassist.js','module-aiasm.js','module-smartctx.js','module-duckduckgo.js','module-fetcher.js','module-chatstudio.js'], false], - ['Extensions', ['calc.js','voice.js','cron.js','theme.js','wpm.js','mindmap.js','webdav.js','skill-pack-core.js','editor.js','diff.js','alias.js','finder.js','filemgr.js','cryptogzip.js','tutorial.js','shell.js','busybox.js','floating.js','console.js','kanban.js'], false] + ['Extensions', ['calc.js','voice.js','cron.js','theme.js','wpm.js','mindmap.js','webdav.js','skill-pack-core.js','editor.js','diff.js','alias.js','finder.js','filemgr.js','cryptogzip.js','tutorial.js','shell.js','busybox.js','floating.js','console.js','kanban.js','course.js'], false] ]; var isSel = function(name) { return true; }; // default: all selected mods.forEach(function(section) { @@ -1345,13 +1349,13 @@ </script> <!-- v4.5.63: A2A scratchpad fixes + agent-register arg fix --> -<script src="modules/core/core-bundle.js?v=v4.6.26"></script> +<script src="modules/core/core-bundle.js?v=v4.6.27"></script> <!-- Async dependency-ordered module loader now only initializes terminal and loads optional ext modules --> -<script src="modules/core/module-loader.js?v=v4.6.26"></script> +<script src="modules/core/module-loader.js?v=v4.6.27"></script> <!-- Critical ext bundle loads AFTER webx11 exists so registerModule calls work --> -<script src="modules/ext/critical-ext-bundle.js?v=v4.6.26"></script> +<script src="modules/ext/critical-ext-bundle.js?v=v4.6.27"></script> <script> // ═══ ONE TRUE VERSION SOURCE ═══ diff --git a/modules-index.json b/modules-index.json index ffa4efd..6d562f2 100644 --- a/modules-index.json +++ b/modules-index.json @@ -1,908 +1,1093 @@ { - "version": "2026-06-30-v4.5.83", - "generated": "2026-06-30T00:00:00.000Z", - "modules": { - "module-agent-registry": { - "name": "module-agent-registry", - "file": "modules/core/module-agent-registry.js", - "type": "core", - "class": "AgentRegistryModule", - "auto_load": true, - "commands": [ - "agent-list", - "agent-status", - "agent-register", - "agent-unregister", - "contract-register", - "contract-list" - ], - "description": "module-agent-registry.js \u2014 VFS-backed agent directory with A2A contracts", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "core" - }, - "module-aiasm": { - "name": "module-aiasm", - "file": "modules/core/module-aiasm.js", - "type": "core", - "class": "WebXAIAliasModule", - "auto_load": true, - "commands": [ - "webxai", - "wx", - "webx" - ], - "description": "Extracted from newOS4 index.html \u2014 WebXAIAliasModule", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "core" - }, - "module-aiassist": { - "name": "module-aiassist", - "file": "modules/core/module-aiassist.js", - "type": "core", - "class": "WebX11AIAssistantModule", - "auto_load": true, - "commands": [ - "ai", - "ask", - "ai-clear", - "ai-system", - "ai-context", - "ai-allow", - "ai-deny", - "ai-ignore", - "ai-stop", - "ai-model" - ], - "description": "Extracted from newOS4 index.html \u2014 WebX11AIAssistantModule (Full Agent Loop)", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "core" - }, - "module-chatstudio": { - "name": "module-chatstudio", - "file": "modules/core/module-chatstudio.js", - "type": "core", - "class": "ChatStudioModule", - "auto_load": true, - "commands": [ - "chat" - ], - "description": "Load CSS", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "core" - }, - "module-config": { - "name": "module-config", - "file": "modules/core/module-config.js", - "type": "core", - "class": "ConfigManagerModule", - "auto_load": true, - "commands": [ - "config", - "config-set", - "config-list", - "config-delete", - "config-export", - "config-reset" - ], - "description": "Extracted from newOS4 index.html \u2014 ConfigManagerModule", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "core" - }, - "module-fetcher": { - "name": "module-fetcher", - "file": "modules/core/module-fetcher.js", - "type": "core", - "class": "WebFetcherModule", - "auto_load": true, - "commands": [ - "fetch", - "web", - "extract" - ], - "description": "example.com\", \"info\");", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "core" - }, - "module-fs": { - "name": "module-fs", - "file": "modules/core/module-fs.js", - "type": "core", - "class": "FileSystemModule", - "auto_load": true, - "commands": [ - "ls", - "cat", - "put", - "rm", - "trash", - "trash-dir", - "ls-trash", - "empty-trash", - "mkdir", - "rmdir", - "mv", - "cp", - "cd", - "pwd", - "find", - "grep", - "head", - "tail", - "sort", - "wc", - "touch", - "tree", - "du", - "upload", - "download", - "vfs-export", - "vfs-import" - ], - "description": "Use trash for recursive dir removal too", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "core" - }, - "module-leases": { - "name": "module-leases", - "file": "modules/core/module-leases.js", - "type": "core", - "class": "LeaseModule", - "auto_load": true, - "commands": [ - "lease-list", - "lease-create", - "lease-revoke" - ], - "description": "module-leases.js \u2014 Lease system for agent task delegation", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "core" - }, - "module-loader": { - "name": "module-loader", - "file": "modules/core/module-loader.js", - "type": "core", - "class": null, - "auto_load": true, - "commands": [], - "description": "Async dependency-ordered module loader (foundation, not a module class)", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-08", - "layer": "infrastructure" - }, - "module-manager": { - "name": "module-manager", - "file": "modules/core/module-manager.js", - "type": "core", - "class": "ModuleManager", - "auto_load": true, - "commands": [ - "modules", - "listmods", - "loadmod", - "removemod", - "startup-modules", - "load-local", - "loadlocal" - ], - "description": "Extracted from newOS4 index.html \u2014 ModuleManager", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "infrastructure" - }, - "module-modmgr": { - "name": "module-modmgr", - "file": "modules/core/module-modmgr.js", - "type": "core", - "class": "ModuleManagerModule", - "auto_load": false, - "commands": [ - "modules", - "listmods", - "loadmod", - "removemod", - "startup-modules", - "load-local", - "loadlocal" - ], - "description": "DEPRECATED 2026-06-08: duplicate of module-manager.js. Removed from BUILTIN. File kept for one release for rollback safety \u2014 will be deleted in v4.6.0.", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-08", - "layer": "deprecated" - }, - "module-neural-vfs": { - "name": "module-neural-vfs", - "file": "modules/core/module-neural-vfs.js", - "type": "core", - "class": "NeuralVFSModule", - "auto_load": true, - "commands": [ - "nvfs-index", - "nvfs-status", - "nvfs-resolve", - "nvfs-tree" - ], - "description": "\u2500\u2500 Core Index Builder \u2500\u2500", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "core" - }, - "module-ollama": { - "name": "module-ollama", - "file": "modules/core/module-ollama.js", - "type": "core", - "class": "OllamaModule", - "auto_load": true, - "commands": [ - "ollama", - "ollamaconnect", - "ollamadisconnect", - "ollamamodels", - "ollamasetmodel", - "ollamasetproxy", - "models", - "model-info", - "model-pull", - "model-delete", - "model-run", - "ai", - "ask" - ], - "description": "Ollama model management \u2014 connect, list models, pull/delete, run inference", - "dependencies": [], - "version": "2.0.0", - "updated": "2026-06-04", - "layer": "core" - }, - "module-orchestrator": { - "name": "module-orchestrator", - "file": "modules/core/module-orchestrator.js", - "type": "core", - "class": "OrchestratorModule", - "auto_load": true, - "commands": [ - "orchestrate", - "orchestrator-status", - "orchestrator-spawn", - "orchestrator-kill", - "orchestrator-purge" - ], - "description": "module-orchestrator.js \u2014 Multi-agent orchestration system", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "core" - }, - "module-searxng": { - "name": "module-searxng", - "file": "modules/core/module-searxng.js", - "type": "core", - "class": "SearXNGModule", - "auto_load": true, - "commands": [ - "search", - "s", - "images", - "videos" - ], - "description": "", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "core" - }, - "module-servermon": { - "name": "module-servermon", - "file": "modules/core/module-servermon.js", - "type": "core", - "class": "ServerMonitorModule", - "auto_load": true, - "commands": [ - "server", - "servers", - "server-check", - "server-remove", - "monitor", - "system-health" - ], - "description": "module-servermon.js \u2014 Server monitoring + self-optimization telemetry", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "core" - }, - "module-simplegui": { - "name": "module-simplegui", - "file": "modules/core/module-simplegui.js", - "type": "core", - "class": "SimpleGUIModule", - "auto_load": true, - "commands": [ - "gui", - "gui-button", - "gui-input", - "gui-text", - "gui-clear", - "gui-run" - ], - "description": "Extracted from newOS4 index.html \u2014 SimpleGUIModule", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "core" - }, - "module-system": { - "name": "module-system", - "file": "modules/core/module-system.js", - "type": "core", - "class": "SystemModule", - "auto_load": true, - "commands": [ - "help", - "echo", - "mem", - "status", - "preview", - "factory-reset", - "reset-vfs", - "logs", - "logrotate", - "health-check", - "memory-map-export", - "font-size", - "setfont", - "smart-prompt", - "checkpoint", - "checkpoint-list", - "rollback", - "checkpoint-del", - "commands", - "check" - ], - "description": "Extracted from newOS4 index.html \u2014 SystemModule", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "core" - }, - "ollama-manager": { - "name": "ollama-manager", - "file": "modules/core/ollama-manager.js", - "type": "core", - "class": "OllamaManager", - "auto_load": true, - "commands": [ - "ollamaconnect", - "ollamamodels", - "model", - "models" - ], - "description": "Extracted from newOS4 index.html \u2014 OllamaManager / LLMRouter", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "infrastructure" - }, - "secret-vault": { - "name": "secret-vault", - "file": "modules/core/secret-vault.js", - "type": "core", - "class": "SecretVault", - "auto_load": true, - "commands": [ - "secret-set", - "secret-get", - "secret-list", - "secret-del", - "secret-lock", - "secret-unlock", - "secret-setpin", - "secret-status" - ], - "description": "Extracted from newOS4 index.html \u2014 SecretVault", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "core" - }, - "terminal": { - "name": "terminal", - "file": "modules/core/terminal.js", - "type": "core", - "class": "WebX11SecurityManager", - "auto_load": true, - "commands": [ - "clear", - "help", - "security", - "security-level", - "cmd-allow", - "cmd-deny", - "cd" - ], - "description": "Extracted from newOS4 index.html \u2014 WebX11Terminal", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "infrastructure" - }, - "virtual-fs": { - "name": "virtual-fs", - "file": "modules/core/virtual-fs.js", - "type": "core", - "class": "VirtualFileSystem", - "auto_load": true, - "commands": [ - "save", - "load", - "import" - ], - "description": "concept:// listing", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "infrastructure" - }, - "alias": { - "name": "alias", - "file": "modules/ext/alias.js", - "type": "ext", - "class": "AliasModule", - "auto_load": false, - "commands": [ - "alias", - "unalias" - ], - "description": "Register management commands", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "ext" - }, - "busybox": { - "name": "busybox", - "file": "modules/ext/busybox.js", - "type": "ext", - "class": "BusyBoxModule", - "auto_load": false, - "commands": [ - "which", - "env", - "date", - "cal", - "yes", - "seq", - "base64", - "printf", - "cut", - "uniq", - "hostname", - "whoami", - "basename", - "dirname", - "sleep", - "expand" - ], - "description": "\u2500\u2500 which \u2500\u2500", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "ext" - }, - "calc": { - "name": "calc", - "file": "modules/ext/calc.js", - "type": "ext", - "class": "CalcModule", - "auto_load": false, - "commands": [ - "calc" - ], - "description": "webx11.duckdns.org/spaces/newos4/modules/ext/calc.js');", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "ext" - }, - "cron": { - "name": "cron", - "file": "modules/ext/cron.js", - "type": "ext", - "class": "CronModule", - "auto_load": false, - "commands": [ - "cron-every", - "cron-list", - "cron-stop", - "cron-clear" - ], - "description": "webx11.duckdns.org/spaces/newos4/modules/ext/cron.js');", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "ext" - }, - "cryptogzip": { - "name": "cryptogzip", - "file": "modules/ext/cryptogzip.js", - "type": "ext", - "class": "CryptoGzipModule", - "auto_load": false, - "commands": [ - "gzip", - "gunzip", - "encrypt", - "decrypt" - ], - "description": "CryptoGzipModule \u2014 VFS-level gzip/encrypt commands for WebX11 newOS4", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "ext" - }, - "diff": { - "name": "diff", - "file": "modules/ext/diff.js", - "type": "ext", - "class": "DiffModule", - "auto_load": false, - "commands": [ - "diff" - ], - "description": "Strip trailing empty line if file ends with newline", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "ext" - }, - "editor": { - "name": "editor", - "file": "modules/ext/editor.js", - "type": "ext", - "class": "EditorModule", - "auto_load": false, - "commands": [ - "edit" - ], - "description": "Read existing file content or use empty for new files", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "ext" - }, - "filemgr": { - "name": "filemgr", - "file": "modules/ext/filemgr.js", - "type": "ext", - "class": "FileManagerModule", - "auto_load": false, - "commands": [ - "files", - "preview" - ], - "description": "code blocks first", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "ext" - }, - "finder": { - "name": "finder", - "file": "modules/ext/finder.js", - "type": "ext", - "class": "FinderModule", - "auto_load": false, - "commands": [ - "find" - ], - "description": "webx11.duckdns.org/spaces/newos4/modules/ext/finder.js');", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "ext" - }, - "mindmap": { - "name": "mindmap", - "file": "modules/ext/mindmap.js", - "type": "ext", - "class": "MindmapModule", - "auto_load": false, - "commands": [ - "mindmap", - "svg" - ], - "description": "Interactive mindmap creation with inline SVG rendering", - "dependencies": [], - "version": "2.0.0", - "updated": "2026-06-04", - "layer": "ext" - }, - "shell": { - "name": "shell", - "file": "modules/ext/shell.js", - "type": "ext", - "class": "ShellModule", - "auto_load": false, - "commands": [ - "export", - "unset", - "script", - "$", - "set" - ], - "description": "\u2500\u2500 Hook into terminal.executeCommand \u2500\u2500", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "ext" - }, - "skill-pack-core": { - "name": "skill-pack-core", - "file": "modules/ext/skill-pack-core.js", - "type": "ext", - "class": "SkillPackCoreModule", - "auto_load": false, - "commands": [ - "skill-pack-core" - ], - "description": "Update existing skill content on fresh load (cache-bust version bump triggers this)", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "ext" - }, - "theme": { - "name": "theme", - "file": "modules/ext/theme.js", - "type": "ext", - "class": "ThemeModule", - "auto_load": false, - "commands": [ - "theme" - ], - "description": "Cancel any active preview", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "ext" - }, - "tutorial": { - "name": "tutorial", - "file": "modules/ext/tutorial.js", - "type": "ext", - "class": "TutorialModule", - "auto_load": false, - "commands": [ - "tutorial" - ], - "description": "Restore saved state or start fresh", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "ext" - }, - "voice": { - "name": "voice", - "file": "modules/ext/voice.js", - "type": "ext", - "class": "VoiceModule", - "auto_load": false, - "commands": [ - "voice" - ], - "description": "webx11.duckdns.org/spaces/newos4/modules/ext/voice.js');", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "ext" - }, - "webdav": { - "name": "webdav", - "file": "modules/ext/webdav.js", - "type": "ext", - "class": "WebDAVModule", - "auto_load": false, - "commands": [ - "webdav-config", - "webdav-status", - "webdav-ls", - "webdav-push", - "webdav-pull", - "webdav-backup", - "webdav-restore", - "webdav-backup-list", - "webdav-browser", - "webdav-rm", - "webdav-mkdir", - "webdav-rename", - "webdav-move" - ], - "description": "{ url, username, password }", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "ext" - }, - "wpm": { - "name": "wpm", - "file": "modules/ext/wpm.js", - "type": "ext", - "class": "WpmModule", - "auto_load": false, - "commands": [ - "wpm" - ], - "description": "webx11.duckdns.org/spaces/newos4/packages.json';", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-03", - "layer": "ext" - }, - "test-suite": { - "class": "TestSuiteModule", - "file": "modules/test/test-suite.js", - "layer": "ext", - "description": "32 automated tests: filesystem, busybox, shell, cryptogzip, skills, security, modules. Load manually with load-local then test", - "commands": [ - "test" - ], - "manual_load": true - }, - "module-sandbox": { - "class": "SandboxModule", - "file": "modules/core/module-sandbox.js", - "layer": "core", - "description": "WebWorker sandbox pool for isolated task execution", - "commands": [ - "sandbox-status", - "sandbox-config", - "sandbox-run" - ] - }, - "module-smartctx": { - "class": "SmartContextModule", - "file": "modules/core/module-smartctx.js", - "layer": "core", - "description": "AI context profiles — set AI behavior (coding, debug, creative, technical, chat)", - "commands": [ - "context", - "contexts", - "context-new", - "context-delete", - "smart-ask" - ] - }, - "sandbox-worker": { - "class": "SandboxWorker", - "file": "modules/core/sandbox-worker.js", - "layer": "infrastructure", - "description": "WebWorker entry point for sandboxed agent contexts", - "commands": [] - }, - "module-memory-graph": { - "class": "MemoryGraphModule", - "file": "modules/core/module-memory-graph.js", - "layer": "core", - "description": "Cross-session persistent memory graph \u2014 nodes for files, concepts, tasks, tools with weighted edges", - "commands": [ - "memgraph-status", - "memgraph-search", - "memgraph-stats", - "memgraph-export", - "memgraph-prune" - ] - }, - "module-predictor": { - "class": "PredictorModule", - "file": "modules/core/module-predictor.js", - "layer": "core", - "description": "Predictive resource allocation \u2014 estimates task duration and tool turns from historical data", - "commands": [ - "predict", - "predictor-status", - "predictor-config" - ] - }, - "module-world-model": { - "class": "WorldModelModule", - "file": "modules/core/module-world-model.js", - "layer": "core", - "description": "Global World Model \u2014 system self-awareness synthesis from all subsystems", - "commands": [ - "world-model", - "world-model-summary", - "world-model-ask", - "world-model-refresh", - "world-model-config" - ] - }, - "media": { - "name": "media", - "file": "modules/ext/media.js", - "type": "ext", - "class": "MediaModule", - "auto_load": true, - "commands": [ - "analyze" - ], - "description": "media.js \u2014 ext module", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-18", - "layer": "ext" - }, - "console": { - "name": "console", - "file": "modules/ext/console.js", - "type": "ext", - "class": "JSConsoleModule", - "auto_load": true, - "commands": [ - "js", - "js-run", - "node" - ], - "description": "console.js \u2014 ext module", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-18", - "layer": "ext" - }, - "floating": { - "name": "floating", - "file": "modules/ext/floating.js", - "type": "ext", - "class": "FloatingModule", - "auto_load": true, - "commands": [ - "floating-add", - "floating-remove", - "floating-list", - "floating-pos", - "floating-clear", - "floating-toggle" - ], - "description": "floating.js \u2014 ext module", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-18", - "layer": "ext" - }, - "debug-panel": { - "name": "debug-panel", - "file": "modules/ext/debug-panel.js", - "type": "ext", - "class": "DebugPanel", - "auto_load": true, - "commands": [ - "debug", - "debug-ai" - ], - "description": "debug-panel.js \u2014 ext module", - "dependencies": [], - "version": "1.0.0", - "updated": "2026-06-18", - "layer": "ext" - } - }, - "total": 45 + "version": "2026-10-18-atlas", + "generated": "2026-06-30T00:00:00.000Z", + "modules": { + "module-agent-registry": { + "name": "module-agent-registry", + "file": "modules/core/module-agent-registry.js", + "type": "core", + "class": "AgentRegistryModule", + "auto_load": true, + "commands": [ + "a2a-ack", + "a2a-poll", + "a2a-post", + "a2a-prune", + "a2a-reject", + "a2a-status", + "agent-list", + "agent-register", + "agent-status", + "agent-unregister", + "contract-list", + "contract-register" + ], + "description": "module-agent-registry.js — VFS-backed agent directory with A2A contracts", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "core", + "tier": "core-bundle" + }, + "module-aiasm": { + "name": "module-aiasm", + "file": "modules/core/module-aiasm.js", + "type": "core", + "class": "WebXAIAliasModule", + "auto_load": true, + "commands": [ + "webx", + "webxai", + "wx" + ], + "description": "Extracted from newOS4 index.html — WebXAIAliasModule", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "core", + "tier": "core-bundle" + }, + "module-aiassist": { + "name": "module-aiassist", + "file": "modules/core/module-aiassist.js", + "type": "core", + "class": "WebX11AIAssistantModule", + "auto_load": true, + "commands": [ + "agent-loops", + "ai", + "ai-allow", + "ai-clear", + "ai-compress", + "ai-config", + "ai-context", + "ai-deny", + "ai-ignore", + "ai-model", + "ai-stop", + "ai-system", + "archive-list", + "archive-read", + "ask", + "bench", + "bench-compare", + "commit-approve", + "commit-deny", + "commit-status", + "debug", + "debug-ai", + "debug-ai-last", + "debug-ai-log", + "debug-ai-send", + "guardrail", + "guardrail-add", + "guardrail-remove", + "mem-debug", + "reset-mem", + "shadow", + "shadow-commit", + "shadow-rollback", + "vaultmind-connect", + "vaultmind-query", + "vaultmind-status", + "wallet-lookup" + ], + "description": "Extracted from newOS4 index.html — WebX11AIAssistantModule (Full Agent Loop)", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "core", + "tier": "core-bundle" + }, + "module-chatstudio": { + "name": "module-chatstudio", + "file": "modules/core/module-chatstudio.js", + "type": "core", + "class": "ChatStudioModule", + "auto_load": true, + "commands": [ + "chat" + ], + "description": "Load CSS", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "core", + "tier": "core-bundle" + }, + "module-config": { + "name": "module-config", + "file": "modules/core/module-config.js", + "type": "core", + "class": "ConfigManagerModule", + "auto_load": true, + "commands": [ + "config", + "config-delete", + "config-export", + "config-list", + "config-reset", + "config-set" + ], + "description": "Extracted from newOS4 index.html — ConfigManagerModule", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "core", + "tier": "core-bundle" + }, + "module-fetcher": { + "name": "module-fetcher", + "file": "modules/core/module-fetcher.js", + "type": "core", + "class": "WebFetcherModule", + "auto_load": true, + "commands": [ + "extract", + "fetch" + ], + "description": "example.com\", \"info\");", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "core", + "tier": "core-bundle" + }, + "module-fs": { + "name": "module-fs", + "file": "modules/core/module-fs.js", + "type": "core", + "class": "FileSystemModule", + "auto_load": true, + "commands": [ + "cat", + "cd", + "cp", + "download", + "du", + "echo", + "empty-trash", + "find", + "grep", + "head", + "ls", + "ls-trash", + "mkdir", + "mv", + "put", + "pwd", + "rm", + "rmdir", + "sed", + "session-delete", + "session-export", + "session-import", + "session-list", + "session-load", + "session-new", + "session-save", + "sort", + "tail", + "touch", + "trash", + "trash-dir", + "tree", + "upload", + "vfs-export", + "vfs-import", + "vfs-size", + "wc" + ], + "description": "Use trash for recursive dir removal too", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "core", + "tier": "core-bundle" + }, + "module-leases": { + "name": "module-leases", + "file": "modules/core/module-leases.js", + "type": "core", + "class": "LeaseModule", + "auto_load": true, + "commands": [ + "lease-create", + "lease-list", + "lease-renew", + "lease-revoke" + ], + "description": "module-leases.js — Lease system for agent task delegation", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "core", + "tier": "core-bundle" + }, + "module-loader": { + "name": "module-loader", + "file": "modules/core/module-loader.js", + "type": "core", + "class": null, + "auto_load": true, + "commands": [], + "description": "Async dependency-ordered module loader (foundation, not a module class)", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-08", + "layer": "infrastructure" + }, + "module-manager": { + "name": "module-manager", + "file": "modules/core/module-manager.js", + "type": "core", + "class": "ModuleManager", + "auto_load": true, + "commands": [ + "modules", + "listmods", + "loadmod", + "removemod", + "startup-modules", + "load-local", + "loadlocal" + ], + "description": "Extracted from newOS4 index.html — ModuleManager", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "infrastructure" + }, + "module-modmgr": { + "name": "module-modmgr", + "file": "modules/core/module-modmgr.js", + "type": "core", + "class": "ModuleManagerModule", + "auto_load": false, + "commands": [ + "listmods", + "loadmod", + "module-disable", + "module-enable", + "module-info", + "module-list", + "module-load", + "module-reload", + "module-reset", + "module-stats", + "module-unload", + "modules", + "modules-list", + "removemod" + ], + "description": "DEPRECATED 2026-06-08: duplicate of module-manager.js. Removed from BUILTIN. File kept for one release for rollback safety — will be deleted in v4.6.0.", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-08", + "layer": "deprecated", + "tier": "core-bundle" + }, + "module-neural-vfs": { + "name": "module-neural-vfs", + "file": "modules/core/module-neural-vfs.js", + "type": "core", + "class": "NeuralVFSModule", + "auto_load": true, + "commands": [ + "nvfs-alias", + "nvfs-cancel", + "nvfs-config", + "nvfs-index", + "nvfs-index-graph", + "nvfs-resolve", + "nvfs-save", + "nvfs-status", + "nvfs-sync", + "nvfs-tree" + ], + "description": "── Core Index Builder ──", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "core", + "tier": "core-bundle" + }, + "module-ollama": { + "name": "module-ollama", + "file": "modules/core/module-ollama.js", + "type": "core", + "class": "OllamaModule", + "auto_load": true, + "commands": [ + "ai", + "ask", + "model-delete", + "model-info", + "model-pull", + "model-run", + "models", + "ollama", + "ollamaconnect", + "ollamadisconnect", + "ollamamodels", + "ollamasetmodel", + "ollamasetproxy" + ], + "description": "Ollama model management — connect, list models, pull/delete, run inference", + "dependencies": [], + "version": "2.0.0", + "updated": "2026-06-04", + "layer": "core", + "tier": "core-bundle" + }, + "module-orchestrator": { + "name": "module-orchestrator", + "file": "modules/core/module-orchestrator.js", + "type": "core", + "class": "OrchestratorModule", + "auto_load": true, + "commands": [ + "a2a-list", + "a2a-send", + "consensus", + "consensus-history", + "consensus-status", + "orchestrate", + "orchestrate-decompose", + "orchestrate-parallel", + "orchestrate-result", + "orchestrator-config", + "orchestrator-kill", + "orchestrator-list", + "orchestrator-purge", + "orchestrator-restart-policy", + "orchestrator-schedule", + "orchestrator-schedules", + "orchestrator-sleep", + "orchestrator-spawn", + "orchestrator-status", + "orchestrator-unschedule", + "orchestrator-wake", + "pipeline" + ], + "description": "module-orchestrator.js — Multi-agent orchestration system", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "core", + "tier": "core-bundle" + }, + "module-searxng": { + "name": "module-searxng", + "file": "modules/core/module-searxng.js", + "type": "core", + "class": "SearXNGModule", + "auto_load": true, + "commands": [ + "images", + "s", + "search", + "videos" + ], + "description": "", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "core", + "tier": "core-bundle" + }, + "module-servermon": { + "name": "module-servermon", + "file": "modules/core/module-servermon.js", + "type": "core", + "class": "ServerMonitorModule", + "auto_load": true, + "commands": [ + "monitor", + "server", + "server-check", + "server-remove", + "servers", + "system-health", + "test-net" + ], + "description": "module-servermon.js — Server monitoring + self-optimization telemetry", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "core", + "tier": "core-bundle" + }, + "module-simplegui": { + "name": "module-simplegui", + "file": "modules/core/module-simplegui.js", + "type": "core", + "class": "SimpleGUIModule", + "auto_load": true, + "commands": [ + "gui", + "gui-button", + "gui-clear", + "gui-input", + "gui-run", + "gui-text" + ], + "description": "Extracted from newOS4 index.html — SimpleGUIModule", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "core", + "tier": "core-bundle" + }, + "module-system": { + "name": "module-system", + "file": "modules/core/module-system.js", + "type": "core", + "class": "SystemModule", + "auto_load": true, + "commands": [ + "bug-report", + "checkpoint", + "checkpoint-del", + "checkpoint-list", + "doctor", + "echo", + "errors", + "factory-reset", + "factory-reset-old", + "font-size", + "health-check", + "help", + "logrotate", + "logs", + "mem", + "memory-map-export", + "preview", + "reset", + "reset-vfs", + "rollback", + "setfont", + "status", + "uptime", + "version" + ], + "description": "Extracted from newOS4 index.html — SystemModule", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "core", + "tier": "core-bundle" + }, + "ollama-manager": { + "name": "ollama-manager", + "file": "modules/core/ollama-manager.js", + "type": "core", + "class": "OllamaManager", + "auto_load": true, + "commands": [ + "ollamaconnect", + "ollamamodels", + "model", + "models" + ], + "description": "Extracted from newOS4 index.html — OllamaManager / LLMRouter", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "infrastructure" + }, + "secret-vault": { + "name": "secret-vault", + "file": "modules/core/secret-vault.js", + "type": "core", + "class": "SecretVault", + "auto_load": true, + "commands": [ + "secret-del", + "secret-get", + "secret-list", + "secret-lock", + "secret-set", + "secret-setpin", + "secret-status", + "secret-unlock" + ], + "description": "Extracted from newOS4 index.html — SecretVault", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "core", + "tier": "core-bundle" + }, + "terminal": { + "name": "terminal", + "file": "modules/core/terminal.js", + "type": "core", + "class": "WebX11SecurityManager", + "auto_load": true, + "commands": [ + "clear", + "help", + "security", + "security-level", + "cmd-allow", + "cmd-deny", + "cd" + ], + "description": "Extracted from newOS4 index.html — WebX11Terminal", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "infrastructure" + }, + "virtual-fs": { + "name": "virtual-fs", + "file": "modules/core/virtual-fs.js", + "type": "core", + "class": "VirtualFileSystem", + "auto_load": true, + "commands": [ + "save", + "load", + "import" + ], + "description": "concept:// listing", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "infrastructure" + }, + "alias": { + "name": "alias", + "file": "modules/ext/alias.js", + "type": "ext", + "class": "AliasModule", + "auto_load": false, + "commands": [ + "alias", + "unalias" + ], + "description": "Register management commands", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "ext", + "tier": "critical-ext-bundle" + }, + "busybox": { + "name": "busybox", + "file": "modules/ext/busybox.js", + "type": "ext", + "class": "BusyBoxModule", + "auto_load": false, + "commands": [ + "base64", + "basename", + "cal", + "cut", + "date", + "dirname", + "env", + "expand", + "hostname", + "printf", + "seq", + "sleep", + "uniq", + "which", + "whoami", + "yes" + ], + "description": "── which ──", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "ext", + "tier": "critical-ext-bundle" + }, + "calc": { + "name": "calc", + "file": "modules/ext/calc.js", + "type": "ext", + "class": "CalcModule", + "auto_load": false, + "commands": [ + "calc" + ], + "description": "webx11.duckdns.org/spaces/newos4/modules/ext/calc.js');", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "ext", + "tier": "ext-auto" + }, + "cron": { + "name": "cron", + "file": "modules/ext/cron.js", + "type": "ext", + "class": "CronModule", + "auto_load": false, + "commands": [ + "cron-clear", + "cron-every", + "cron-list", + "cron-stop" + ], + "description": "webx11.duckdns.org/spaces/newos4/modules/ext/cron.js');", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "ext", + "tier": "ext-auto" + }, + "cryptogzip": { + "name": "cryptogzip", + "file": "modules/ext/cryptogzip.js", + "type": "ext", + "class": "CryptoGzipModule", + "auto_load": false, + "commands": [ + "decrypt", + "encrypt", + "gunzip", + "gzip", + "tar", + "untar", + "wget" + ], + "description": "CryptoGzipModule — VFS-level gzip/encrypt commands for WebX11 newOS4", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "ext", + "tier": "ext-auto" + }, + "diff": { + "name": "diff", + "file": "modules/ext/diff.js", + "type": "ext", + "class": "DiffModule", + "auto_load": false, + "commands": [ + "diff" + ], + "description": "Strip trailing empty line if file ends with newline", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "ext", + "tier": "critical-ext-bundle" + }, + "editor": { + "name": "editor", + "file": "modules/ext/editor.js", + "type": "ext", + "class": "EditorModule", + "auto_load": false, + "commands": [ + "edit" + ], + "description": "Read existing file content or use empty for new files", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "ext", + "tier": "critical-ext-bundle" + }, + "filemgr": { + "name": "filemgr", + "file": "modules/ext/filemgr.js", + "type": "ext", + "class": "FileManagerModule", + "auto_load": false, + "commands": [ + "files", + "preview" + ], + "description": "code blocks first", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "ext", + "tier": "critical-ext-bundle" + }, + "finder": { + "name": "finder", + "file": "modules/ext/finder.js", + "type": "ext", + "class": "FinderModule", + "auto_load": false, + "commands": [ + "find" + ], + "description": "webx11.duckdns.org/spaces/newos4/modules/ext/finder.js');", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "ext", + "tier": "critical-ext-bundle" + }, + "mindmap": { + "name": "mindmap", + "file": "modules/ext/mindmap.js", + "type": "ext", + "class": "MindmapModule", + "auto_load": false, + "commands": [ + "mindmap" + ], + "description": "Interactive mindmap creation with inline SVG rendering", + "dependencies": [], + "version": "2.0.0", + "updated": "2026-06-04", + "layer": "ext", + "tier": "ext-auto" + }, + "shell": { + "name": "shell", + "file": "modules/ext/shell.js", + "type": "ext", + "class": "ShellModule", + "auto_load": false, + "commands": [ + "export", + "script", + "set", + "sh", + "unset" + ], + "description": "── Hook into terminal.executeCommand ──", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "ext", + "tier": "critical-ext-bundle" + }, + "skill-pack-core": { + "name": "skill-pack-core", + "file": "modules/ext/skill-pack-core.js", + "type": "ext", + "class": "SkillPackCoreModule", + "auto_load": false, + "commands": [ + "learn-skill", + "skill-audit", + "skill-create", + "skill-delete", + "skill-export", + "skill-import", + "skill-list", + "skill-pack-core", + "skill-patch", + "skill-restore", + "skill-sync", + "skill-validate", + "skill-view", + "skill-webdav-sync" + ], + "description": "Update existing skill content on fresh load (cache-bust version bump triggers this)", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "ext", + "tier": "ext-auto" + }, + "theme": { + "name": "theme", + "file": "modules/ext/theme.js", + "type": "ext", + "class": "ThemeModule", + "auto_load": false, + "commands": [ + "theme" + ], + "description": "Cancel any active preview", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "ext", + "tier": "ext-auto" + }, + "tutorial": { + "name": "tutorial", + "file": "modules/ext/tutorial.js", + "type": "ext", + "class": "TutorialModule", + "auto_load": false, + "commands": [ + "tutorial" + ], + "description": "Restore saved state or start fresh", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "ext", + "tier": "ext-auto" + }, + "voice": { + "name": "voice", + "file": "modules/ext/voice.js", + "type": "ext", + "class": "VoiceModule", + "auto_load": false, + "commands": [ + "speak", + "voice" + ], + "description": "webx11.duckdns.org/spaces/newos4/modules/ext/voice.js');", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "ext", + "tier": "ext-auto" + }, + "webdav": { + "name": "webdav", + "file": "modules/ext/webdav.js", + "type": "ext", + "class": "WebDAVModule", + "auto_load": false, + "commands": [ + "webdav-backup", + "webdav-backup-list", + "webdav-browser", + "webdav-config", + "webdav-login", + "webdav-ls", + "webdav-mkdir", + "webdav-move", + "webdav-pull", + "webdav-push", + "webdav-rename", + "webdav-restore", + "webdav-rm", + "webdav-status" + ], + "description": "{ url, username, password }", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "ext", + "tier": "ext-auto" + }, + "wpm": { + "name": "wpm", + "file": "modules/ext/wpm.js", + "type": "ext", + "class": "WpmModule", + "auto_load": false, + "commands": [ + "wpm" + ], + "description": "webx11.duckdns.org/spaces/newos4/packages.json';", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-03", + "layer": "ext", + "tier": "ext-auto" + }, + "test-suite": { + "class": "TestSuiteModule", + "file": "modules/test/test-suite.js", + "layer": "ext", + "description": "32 automated tests: filesystem, busybox, shell, cryptogzip, skills, security, modules. Load manually with load-local then test", + "commands": [ + "test" + ], + "manual_load": true + }, + "module-sandbox": { + "class": "SandboxModule", + "file": "modules/core/module-sandbox.js", + "layer": "core", + "description": "WebWorker sandbox pool for isolated task execution", + "commands": [ + "sandbox-config", + "sandbox-run", + "sandbox-status" + ], + "tier": "core-bundle" + }, + "module-smartctx": { + "class": "SmartContextModule", + "file": "modules/core/module-smartctx.js", + "layer": "core", + "description": "AI context profiles — set AI behavior (coding, debug, creative, technical, chat)", + "commands": [ + "context", + "context-auto", + "context-delete", + "context-new", + "contexts", + "smart-ask" + ], + "tier": "core-bundle" + }, + "sandbox-worker": { + "class": "SandboxWorker", + "file": "modules/core/sandbox-worker.js", + "layer": "infrastructure", + "description": "WebWorker entry point for sandboxed agent contexts", + "commands": [] + }, + "module-memory-graph": { + "class": "MemoryGraphModule", + "file": "modules/core/module-memory-graph.js", + "layer": "core", + "description": "Cross-session persistent memory graph — nodes for files, concepts, tasks, tools with weighted edges", + "commands": [ + "memgraph-agent", + "memgraph-decay-config", + "memgraph-export", + "memgraph-learn", + "memgraph-prune", + "memgraph-search", + "memgraph-session", + "memgraph-stats", + "memgraph-status" + ], + "tier": "core-bundle" + }, + "module-predictor": { + "class": "PredictorModule", + "file": "modules/core/module-predictor.js", + "layer": "core", + "description": "Predictive resource allocation — estimates task duration and tool turns from historical data", + "commands": [ + "predict", + "predictor-config", + "predictor-status" + ], + "tier": "core-bundle" + }, + "module-world-model": { + "class": "WorldModelModule", + "file": "modules/core/module-world-model.js", + "layer": "core", + "description": "Global World Model — system self-awareness synthesis from all subsystems", + "commands": [ + "world-model", + "world-model-ask", + "world-model-config", + "world-model-refresh", + "world-model-summary" + ], + "tier": "core-bundle" + }, + "media": { + "name": "media", + "file": "modules/ext/media.js", + "type": "ext", + "class": "MediaModule", + "auto_load": true, + "commands": [ + "analyze" + ], + "description": "media.js — ext module", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-18", + "layer": "ext", + "tier": "ext-auto" + }, + "console": { + "name": "console", + "file": "modules/ext/console.js", + "type": "ext", + "class": "JSConsoleModule", + "auto_load": true, + "commands": [ + "js", + "js-run", + "node" + ], + "description": "console.js — ext module", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-18", + "layer": "ext", + "tier": "ext-auto" + }, + "floating": { + "name": "floating", + "file": "modules/ext/floating.js", + "type": "ext", + "class": "FloatingModule", + "auto_load": true, + "commands": [ + "floating-add", + "floating-clear", + "floating-list", + "floating-pos", + "floating-remove", + "floating-toggle" + ], + "description": "floating.js — ext module", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-18", + "layer": "ext", + "tier": "ext-auto" + }, + "debug-panel": { + "name": "debug-panel", + "file": "modules/ext/debug-panel.js", + "type": "ext", + "class": "DebugPanel", + "auto_load": true, + "commands": [ + "debug" + ], + "description": "debug-panel.js — ext module", + "dependencies": [], + "version": "1.0.0", + "updated": "2026-06-18", + "layer": "ext", + "tier": "ext-auto" + }, + "kanban": { + "name": "kanban", + "file": "modules/ext/kanban.js", + "type": "ext", + "class": "", + "auto_load": true, + "description": "", + "commands": [ + "kanban" + ], + "tier": "ext-auto" + }, + "module-debug-supervisor": { + "name": "module-debug-supervisor", + "file": "modules/core/module-debug-supervisor.js", + "type": "core", + "class": "", + "auto_load": true, + "description": "", + "commands": [ + "debug-analyze", + "debug-fix", + "debug-health", + "debug-history", + "debug-stats", + "debug-sync", + "debug-sync-url", + "debug-trends" + ], + "tier": "core-bundle" + }, + "three": { + "name": "three", + "file": "modules/ext/three.js", + "type": "ext", + "class": "", + "auto_load": true, + "description": "", + "commands": [ + "three" + ], + "tier": "ext-auto" + } + }, + "total": 45 }
\ No newline at end of file diff --git a/modules/core/core-bundle.js b/modules/core/core-bundle.js index dd86773..2714dd3 100644 --- a/modules/core/core-bundle.js +++ b/modules/core/core-bundle.js @@ -4283,9 +4283,17 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm for (const modName in this.moduleManager.modules) { const mod = this.moduleManager.modules[modName]; if (mod.commands && mod.commands[cmd]) { + if (cmd !== 'course') { + for (const _m of Object.values(this.moduleManager.modules)) { + if (typeof _m.onCommandStart === 'function') { try { _m.onCommandStart(cmd, args); } catch (_e) {} } + } + } try { await mod.commands[cmd](args); - // Notify tutorial module for quest tracking - if (this.tutorialModule && cmd !== 'tutorial') this.tutorialModule.onCommandExecuted(cmd, args); + if (cmd !== 'tutorial' && cmd !== 'course') { + for (const _m of Object.values(this.moduleManager.modules)) { + if (typeof _m.onCommandExecuted === 'function') { try { _m.onCommandExecuted(cmd, args); } catch (_e) {} } + } + } // Memory graph: record file access for read/file commands if (cmd === 'cat' || cmd === 'view_module' || cmd === 'edit_module') { try { diff --git a/modules/core/module-loader.js b/modules/core/module-loader.js index 5e69094..53efb08 100644 --- a/modules/core/module-loader.js +++ b/modules/core/module-loader.js @@ -4,7 +4,7 @@ // Core module bundle loaded synchronously in main.html. // This loader only handles version cache-busting, splash, terminal init, and optional ext modules. - const LATEST_VER = 'v4.6.26'; + const LATEST_VER = 'v4.6.27'; const CACHE = '?v=' + LATEST_VER; const stored = localStorage.getItem('webx11_cache_ver'); if (stored !== LATEST_VER) { @@ -53,7 +53,7 @@ setSplash('Initializing terminal (3/3)', total, total, ''); // webx11 already created synchronously above — just init ext modules if (typeof webx11._loadExtModules === 'function') { - let extKeys = ['calc', 'voice', 'debug-panel', 'cron', 'theme', 'wpm', 'mindmap', 'webdav', 'skill-pack-core', 'cryptogzip', 'tutorial', 'floating', 'console', 'media', 'three', 'kanban']; + let extKeys = ['calc', 'voice', 'debug-panel', 'cron', 'theme', 'wpm', 'mindmap', 'webdav', 'skill-pack-core', 'cryptogzip', 'tutorial', 'floating', 'console', 'media', 'three', 'kanban', 'course']; const saved = localStorage.getItem('webx11_startup_modules'); let selected = null; try { if (saved) selected = JSON.parse(saved); } catch (e) {} diff --git a/modules/core/terminal.js b/modules/core/terminal.js index 123c224..425c5d6 100644 --- a/modules/core/terminal.js +++ b/modules/core/terminal.js @@ -1857,9 +1857,17 @@ for (const modName in this.moduleManager.modules) { const mod = this.moduleManager.modules[modName]; if (mod.commands && mod.commands[cmd]) { + if (cmd !== 'course') { + for (const _m of Object.values(this.moduleManager.modules)) { + if (typeof _m.onCommandStart === 'function') { try { _m.onCommandStart(cmd, args); } catch (_e) {} } + } + } try { await mod.commands[cmd](args); - // Notify tutorial module for quest tracking - if (this.tutorialModule && cmd !== 'tutorial') this.tutorialModule.onCommandExecuted(cmd, args); + if (cmd !== 'tutorial' && cmd !== 'course') { + for (const _m of Object.values(this.moduleManager.modules)) { + if (typeof _m.onCommandExecuted === 'function') { try { _m.onCommandExecuted(cmd, args); } catch (_e) {} } + } + } // Memory graph: record file access for read/file commands if (cmd === 'cat' || cmd === 'view_module' || cmd === 'edit_module') { try { diff --git a/modules/ext/course.js b/modules/ext/course.js new file mode 100644 index 0000000..3879cb5 --- /dev/null +++ b/modules/ext/course.js @@ -0,0 +1,323 @@ +class CourseModule { + constructor() { + this.commands = {}; + this.terminal = null; + this.BASE = '/spaces/newos4/course/'; + this.STATE_KEY = 'webx11_course_progress_v1'; + this._manifest = null; + this._atlas = null; + this._chapters = {}; + this._active = null; + this._capOut = ''; + this._capActive = false; + } + + getHelpInfo() { + return { + 'course': 'course [start|list|next|open <id>|check|hint|progress|reset|ask <q>|project [id]|cmds [q]] — Interactive newOS4 course' + }; + } + + registerCommands() { + this.commands['course'] = (args) => this.handle(args || []); + this._warm(); + } + + _v() { + const mod = localStorage.getItem('webx11_cache_ver') || '1'; + return mod + '-' + new Date().toISOString().slice(0, 10); + } + + async _warm() { + try { await this._load(); await this._installSkills(); } catch (e) { /* offline: commands report on use */ } + } + + async _load(force) { + if (this._manifest && !force) return this._manifest; + const bust = this._v(); + const [m, a] = await Promise.all([ + fetch(this.BASE + 'course.json?v=' + bust).then(r => r.json()), + fetch(this.BASE + 'atlas.json?v=' + bust).then(r => r.json()).catch(() => null) + ]); + this._manifest = m; + this._atlas = a; + return m; + } + + async _chapter(id) { + if (this._chapters[id]) return this._chapters[id]; + const ch = await fetch(this.BASE + 'chapters/' + id + '.json?v=' + this._v()).then(r => r.json()); + this._chapters[id] = ch; + return ch; + } + + _progress() { + try { return JSON.parse(localStorage.getItem(this.STATE_KEY)) || { completed: {}, exercisePass: {}, xp: 0 }; } + catch (e) { return { completed: {}, exercisePass: {}, xp: 0 }; } + } + + _save(p) { localStorage.setItem(this.STATE_KEY, JSON.stringify(p)); } + + _printLesson(l, chTitle) { + const t = this.terminal; + t.print('┌─ ' + chTitle + ' › ' + l.title + ' ─', 'header'); + t.print(' 🎯 ' + (l.objective || ''), 'info'); + t.print(' ⏱ ' + (l.minutes || 5) + ' min · commands: ' + (l.commands || []).join(', '), 'text-muted'); + t.print(''); + for (const b of (l.blocks || [])) { + if (b.type === 'cmd') t.print(' $ ' + b.text, 'success'); + else if (b.type === 'code') t.print(' ' + b.text, 'info'); + else if (b.type === 'tip') t.print(' 💡 ' + b.text, 'warning'); + else if (b.type === 'warn') t.print(' ⚠️ ' + b.text, 'error'); + else t.print(' ' + b.text, ''); + } + if (l.exercise) { + t.print(''); + t.print(' 🏋️ Exercise: ' + l.exercise.instruction, 'info'); + t.print(' Type the command(s); completion is detected automatically (or use `course check`).', 'text-muted'); + } + if (l.quiz) { + t.print(''); + t.print(' ❓ ' + l.quiz.q, 'info'); + (l.quiz.options || []).forEach((o, i) => t.print(' ' + (i + 1) + ') ' + o, 'text-muted')); + t.print(' Answer with: `course quiz <number>`', 'text-muted'); + } + } + + async _open(lessonId) { + const m = await this._load(); + for (const ch of m.chapters || []) { + const c = await this._chapter(ch.id); + const l = (c.lessons || []).find(x => x.id === lessonId); + if (!l) continue; + this._active = { lessonId, chapterId: ch.id, chapterTitle: ch.title, lesson: l }; + this._printLesson(l, ch.title); + this._track('lesson_open', { lesson: lessonId }); + return true; + } + this.terminal.print('Lesson not found: ' + lessonId, 'error'); + return false; + } + + _flat(m) { + return (m.chapters || []).map(ch => ({ id: ch.id, title: ch.title, icon: ch.icon || '📘', level: ch.level || 'beginner', lessons: ch.lessons || [] })); + } + + async handle(args) { + const sub = (args[0] || 'start').toLowerCase(); + let m; + try { m = await this._load(); } + catch (e) { return this.terminal.print('Course content unreachable (' + e.message + '). Try `course list` again.', 'error'); } + const p = this._progress(); + if (sub === 'list' || sub === 'chapters') { + this.terminal.print('📚 newOS4 Course — v' + m.version + ' (' + m.updated + ')', 'header'); + for (const ch of this._flat(m)) { + const done = ch.lessons.filter(l => p.completed[l.id]).length; + this.terminal.print(' ' + ch.icon + ' ' + ch.id + ' · ' + ch.title + ' [' + done + '/' + ch.lessons.length + ']', done ? 'success' : ''); + } + this.terminal.print(' course start | open <lessonId> | next | progress', 'text-muted'); + return; + } + if (sub === 'start' || sub === 'next') { + for (const ch of this._flat(m)) for (const l of ch.lessons) if (!p.completed[l.id]) return this._open(l.id); + return this.terminal.print('🎉 Course complete! Try `course project` or `course cmds <query>`.', 'success'); + } + if (sub === 'open') { if (!args[1]) return this.terminal.print('Usage: course open <lessonId>', 'warning'); return this._open(args[1]); } + if (sub === 'hint') { + if (!this._active) return this.terminal.print('Open a lesson first: `course start`.', 'warning'); + return this.terminal.print('💡 ' + (this._active.lesson.exercise?.hint || 'No hint for this lesson.'), 'warning'); + } + if (sub === 'progress') { + const total = this._flat(m).reduce((n, ch) => n + ch.lessons.length, 0); + const done = Object.keys(p.completed).length; + this.terminal.print('📈 Progress: ' + done + '/' + total + ' lessons · ' + (p.xp || 0) + ' XP', 'header'); + const next = this._flat(m).flatMap(c => c.lessons).find(l => !p.completed[l.id]); + if (next) this.terminal.print(' Next: `course open ' + next.id + '` — ' + next.title, 'info'); + return; + } + if (sub === 'reset') { + if (args[1] !== '--confirm') return this.terminal.print('This erases course progress. Run: course reset --confirm', 'warning'); + localStorage.removeItem(this.STATE_KEY); this._active = null; return this.terminal.print('Course progress reset.', 'success'); + } + if (sub === 'quiz') { + if (!this._active || !this._active.lesson.quiz) return this.terminal.print('No quiz open.', 'warning'); + const pick = parseInt(args[1], 10) - 1; + const ok = pick === this._active.lesson.quiz.answer; + if (ok) this._complete(this._active.lessonId, 5); + return this.terminal.print(ok ? '✅ Correct (+5 XP)' : '❌ Not quite — re-read with `course open ' + this._active.lessonId + '`.', ok ? 'success' : 'error'); + } + if (sub === 'check') return this._manualCheck(); + if (sub === 'cmds') return this._cmdSearch(args.slice(1).join(' ')); + if (sub === 'project') return this._project(args[1]); + if (sub === 'ask') return this._ask(args.slice(1).join(' ')); + this.terminal.print('Usage: ' + this.getHelpInfo().course, 'warning'); + } + + _complete(lessonId, xp) { + const p = this._progress(); + if (!p.completed[lessonId]) { + p.completed[lessonId] = new Date().toISOString(); + p.xp = (p.xp || 0) + (xp || 10); + this._save(p); + this.terminal.print('✅ Lesson ' + lessonId + ' complete (+' + (xp || 10) + ' XP)', 'success'); + this._track('lesson_complete', { lesson: lessonId }); + } + } + + _cmdSearch(q) { + if (!this._atlas) return this.terminal.print('Atlas not loaded yet — retry in a moment.', 'warning'); + const items = Object.entries(this._atlas.commands); + const hit = q ? items.filter(([c, e]) => (c + ' ' + e.desc + ' ' + e.module + ' ' + e.category).toLowerCase().includes(q.toLowerCase())) : items; + this.terminal.print('🔎 ' + (q ? hit.length + ' match(es)' : items.length + ' commands') + ' — `course cmds <query>` to filter', 'header'); + for (const [c, e] of hit.slice(0, 40)) this.terminal.print(' ' + c.padEnd(18) + ' ' + (e.desc || e.module).slice(0, 90), 'text-muted'); + } + + onCommandStart(cmd, args) { + if (cmd === 'course') return; + if (this._capActive) { try { this.terminal._popPrintCapture(); } catch (e) {} this._capActive = false; } + this._capOut = ''; + this._capActive = true; + try { this.terminal._pushPrintCapture(txt => { this._capOut = txt; }); } catch (e) { this._capActive = false; } + } + + onCommandExecuted(cmd, args) { + if (this._capActive) { try { this.terminal._popPrintCapture(); } catch (e) {} this._capActive = false; } + if (!this._active || !this._active.lesson.exercise || cmd === 'course') return; + const ex = this._active.lesson.exercise; + const line = (cmd + (args.length ? ' ' + args.join(' ') : '')).trim(); + if (!ex.accept.some(pat => this._match(pat, line))) return; + this._verify(ex, line); + } + + _match(pattern, line) { + const esc = pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*'); + return new RegExp('^' + esc + '$', 'i').test(line); + } + + _verify(ex, line) { + const check = ex.check || { type: 'command' }; + let ok = false, detail = ''; + if (check.type === 'command' || check.type === 'manual') ok = true; + else if (check.type === 'file') { + if (!check.path) { detail = 'exercise misconfigured: check.path missing'; } + else { + let exists = false; + try { const c = this.terminal.fs.readFile(check.path); exists = (c !== null && c !== undefined); } catch (e) { exists = false; } + ok = exists; detail = ok ? '' : 'file not found: ' + check.path; + } + } else if (check.type === 'output') { + ok = (this._capOut || '').toLowerCase().includes((check.contains || '').toLowerCase()); + detail = ok ? '' : 'expected output to contain "' + check.contains + '"'; + } + if (ok) this._complete(this._active.lessonId, 10); + else { this.terminal.print(' ✗ Not done yet — ' + (detail || 'keep trying'), 'warning'); this._track('exercise_fail', { lesson: this._active.lessonId, command: line.slice(0, 120) }); } + } + + _manualCheck() { + if (!this._active) return this.terminal.print('Open a lesson first: `course start`.', 'warning'); + const ex = this._active.lesson.exercise; + if (!ex) return this.terminal.print('This lesson has no exercise.', 'warning'); + this._verify(ex, '(manual check)'); + } + + async _ask(q) { + if (!q) return this.terminal.print('Usage: course ask <question>', 'warning'); + const l = this._active && this._active.lesson; + const sys = 'You are the newOS4 course tutor. Answer in <=120 words. Use ONLY the course context and command atlas below. ' + + 'If something is not covered, say so and point to `help <cmd>` or the relevant lesson — never invent commands.\n\n' + + (l ? this._lessonContext(l) : this._atlasContext(q)); + const model = (this.terminal.ollamaManager && this.terminal.ollamaManager.currentModel) || 'gemma4:31b-cloud'; + const tok = localStorage.getItem('webx11_session_id') || ''; + const headers = { 'Content-Type': 'application/json' }; + if (tok) headers.Authorization = ['Bearer', tok].join(' '); + this.terminal.print('🤖 thinking…', 'text-muted'); + try { + const r = await fetch('/ai/v1/chat/completions', { + method: 'POST', headers, + body: JSON.stringify({ model, stream: false, messages: [{ role: 'system', content: sys }, { role: 'user', content: q }] }) + }); + if (!r.ok) throw new Error('HTTP ' + r.status + (r.status === 401 ? ' — log in to use the AI tutor' : '')); + const d = await r.json(); + const ans = (d.choices && d.choices[0] && d.choices[0].message && d.choices[0].message.content) || '(empty answer)'; + this.terminal.print('🎓 ' + ans.trim(), 'info'); + this._track('question', { q: q.slice(0, 240), lesson: l ? l.id : null }); + } catch (e) { + this.terminal.print('AI tutor unavailable: ' + e.message + '. Use `ai <question>` as a fallback.', 'warning'); + } + } + + _lessonContext(l) { + const lines = ['LESSON: ' + l.title, 'OBJECTIVE: ' + l.objective, 'COMMANDS: ' + (l.commands || []).join(', ')]; + for (const b of (l.blocks || [])) lines.push(b.type.toUpperCase() + ': ' + b.text); + if (l.exercise) lines.push('EXERCISE: ' + l.exercise.instruction + ' | HINT: ' + l.exercise.hint); + if (this._atlas) for (const c of (l.commands || [])) { const e = this._atlas.commands[c]; if (e) lines.push('ATLAS ' + c + ': ' + e.desc + ' [' + e.tier + ']'); } + return lines.join('\n').slice(0, 6000); + } + + _atlasContext(q) { + if (!this._atlas) return 'No atlas loaded.'; + const hits = Object.entries(this._atlas.commands) + .filter(([c, e]) => (c + ' ' + e.desc).toLowerCase().includes((q || '').toLowerCase().split(/\s+/)[0] || '')) + .slice(0, 8).map(([c, e]) => c + ': ' + e.desc); + return 'COMMAND ATLAS (top matches):\n' + hits.join('\n'); + } + + async _project(id) { + const m = await this._load(); + const ids = m.projects || []; + if (!id) return this.terminal.print('🛠 Projects: ' + (ids.join(', ') || 'none yet') + ' — `course project <id>`', 'info'); + const pr = await fetch(this.BASE + 'projects/' + id + '.json?v=' + this._v()).then(r => r.json()); + this.terminal.print('🛠 ' + pr.title + ' (' + pr.minutes + ' min) — ' + pr.goal, 'header'); + (pr.steps || []).forEach((s, i) => this.terminal.print(' ' + (i + 1) + '. ' + s.instruction + (s.command ? ' → ' + s.command : ''), '')); + this._track('project_open', { project: id }); + } + + async _installSkills() { + const fs = this.terminal && this.terminal.fs; + const m = this._manifest; + if (!fs || !m) return; + const key = 'webx11_course_skills_ver'; + if (localStorage.getItem(key) === m.version) return; + try { + fs.mkdirRecursive('/skills'); + const index = fs.fsReadJSON('/skills/index.json') || {}; + let n = 0; + for (const ch of m.chapters || []) { + const c = this._chapters[ch.id] || await this._chapter(ch.id); + const body = (c.lessons || []).map(l => '## ' + l.title + '\n' + (l.blocks || []) + .filter(b => b.type === 'text' || b.type === 'warn' || b.type === 'tip') + .map(b => b.text).join('\n')).join('\n'); + const name = 'course-' + ch.id.replace(/[^a-z0-9-]/gi, '-').toLowerCase(); + const content = '---\nname: ' + name + '\ndescription: newOS4 course — ' + ch.title + + '\ntags: [course, newos4]\ncategory: learning\n---\n' + body; + fs.writeFile('/skills/' + name + '.md', content); + index[name] = { + name, description: 'newOS4 course — ' + ch.title, + tags: ['course', 'newos4'], category: 'learning', + created: (index[name] && index[name].created) || new Date().toISOString(), + updated: new Date().toISOString() + }; + n++; + } + fs.fsWriteJSON('/skills/index.json', index); + localStorage.setItem(key, m.version); + if (n) this.terminal.print('📚 Course skills synced (' + n + ') — the AI can use them via `load_skills`.', 'success'); + } catch (e) { /* VFS busy — retried on next load */ } + } + + _track(type, detail) { + try { + const sid = localStorage.getItem('webx11_course_sid') || (Math.random().toString(36).slice(2) + Date.now().toString(36)); + localStorage.setItem('webx11_course_sid', sid); + if (localStorage.getItem('webx11_course_telemetry') === 'off') return; + fetch('/spaces/newos4/api/course/event.php', { + method: 'POST', headers: { 'Content-Type': 'application/json' }, keepalive: true, + body: JSON.stringify({ sid, type, detail, version: (this._manifest && this._manifest.version) || '' }) + }).catch(() => {}); + } catch (e) {} + } +} + +if (typeof window.webx11 !== 'undefined' && window.webx11) { + webx11.moduleManager.registerModule(new CourseModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/course.js'); +} diff --git a/packages.json b/packages.json index c3488ed..035b0b0 100644 --- a/packages.json +++ b/packages.json @@ -40,7 +40,7 @@ "theme": { "name": "Theme Switcher", "version": "1.0.0", - "desc": "CSS theme manager \u2014 dark, light, amber, cyber + custom", + "desc": "CSS theme manager — dark, light, amber, cyber + custom", "url": "modules/ext/theme.js", "commands": [ "theme" @@ -60,7 +60,7 @@ "webdav": { "name": "WebDAV Sync", "version": "1.0.0", - "desc": "Cloud sync for VFS \u2014 backup, push, pull, and restore via WebDAV", + "desc": "Cloud sync for VFS — backup, push, pull, and restore via WebDAV", "url": "modules/ext/webdav.js", "commands": [ "webdav-config", @@ -77,7 +77,7 @@ "mindmap": { "name": "Mindmap", "version": "1.0.0", - "desc": "Brainstorming mindmap tool \u2014 new, add, show, export, save, load, color, tag", + "desc": "Brainstorming mindmap tool — new, add, show, export, save, load, color, tag", "url": "modules/ext/mindmap.js", "commands": [ "mindmap" @@ -97,7 +97,7 @@ "editor": { "name": "Text Editor", "version": "1.0.0", - "desc": "Inline text editor for VFS files \u2014 edit, save, cancel via modal", + "desc": "Inline text editor for VFS files — edit, save, cancel via modal", "url": "modules/ext/editor.js", "commands": [ "edit" @@ -203,7 +203,7 @@ "floating": { "name": "Floating UI", "version": "1.0.0", - "desc": "Remixable floating UI elements \u2014 buttons, collapsible menus, panels. Persisted in localStorage.", + "desc": "Remixable floating UI elements — buttons, collapsible menus, panels. Persisted in localStorage.", "url": "modules/ext/floating.js", "commands": [ "floating-add", @@ -229,7 +229,7 @@ "debug-panel": { "name": "Debug Panel", "version": "1.0.0", - "desc": "Toggleable debug console \u2014 intercepts console.log/error, fetch failures, JS errors. Copy All + per-line copy.", + "desc": "Toggleable debug console — intercepts console.log/error, fetch failures, JS errors. Copy All + per-line copy.", "url": "modules/ext/debug-panel.js", "commands": [ "debug" @@ -239,7 +239,7 @@ "tutorial": { "name": "Tutorial", "version": "1.0.0", - "desc": "Interactive guided tour \u2014 6 quests to learn newOS4 basics", + "desc": "Interactive guided tour — 6 quests to learn newOS4 basics", "url": "modules/ext/tutorial.js", "commands": [ "tutorial" @@ -259,7 +259,7 @@ "neural-vfs-core": { "name": "Neural VFS (core)", "version": "1.0.0", - "desc": "Core semantic indexer. NOT a WPM package \u2014 built into newos4.tar.gz. Use run_command('nvfs-status') to inspect.", + "desc": "Core semantic indexer. NOT a WPM package — built into newos4.tar.gz. Use run_command('nvfs-status') to inspect.", "url": "modules/core/module-neural-vfs.js", "commands": [ "nvfs-index", @@ -273,7 +273,7 @@ "media": { "name": "Media Analysis", "version": "1.0.0", - "desc": "Image/audio/video analysis via Ollama vision models \u2014 analyze, describe, transcribe", + "desc": "Image/audio/video analysis via Ollama vision models — analyze, describe, transcribe", "url": "modules/ext/media.js", "commands": [ "analyze" @@ -283,7 +283,7 @@ "three": { "name": "3D Scene Builder", "version": "1.0.0", - "desc": "Three.js 3D scene builder \u2014 create, edit, visualize mindmaps in 3D, export standalone HTML", + "desc": "Three.js 3D scene builder — create, edit, visualize mindmaps in 3D, export standalone HTML", "url": "modules/ext/three.js", "commands": [ "three" @@ -292,9 +292,19 @@ }, "kanban": { "url": "https://webx11.duckdns.org/spaces/newos4/modules/ext/kanban.js", - "description": "Kanban board \u2014 todo/doing/done task management with priorities", + "description": "Kanban board — todo/doing/done task management with priorities", "category": "productivity", "size": "6kb" + }, + "course": { + "name": "Interactive Course", + "version": "1.0.0", + "desc": "Self-updating newOS4 course — chapters, walkthroughs, projects, AI tutor", + "url": "modules/ext/course.js", + "commands": [ + "course" + ], + "category": "learning" } } }
\ No newline at end of file diff --git a/scripts/course-daily-update.py b/scripts/course-daily-update.py new file mode 100644 index 0000000..4f509d6 --- /dev/null +++ b/scripts/course-daily-update.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +"""Daily newOS4 course expansion. Grounded only: every command must exist in atlas.json.""" +import json, os, re, sys, subprocess +from datetime import datetime, timezone +import requests + +ROOT = '/var/www/webx11.duckdns.org/spaces/newos4' +COURSE = os.path.join(ROOT, 'course') +ATLAS, FEED, FAQ = (os.path.join(COURSE, n) for n in ('atlas.json', 'feed.json', 'faq.json')) +STATE = os.path.join(COURSE, '_state.json'); CHANGELOG = os.path.join(COURSE, 'changelog.jsonl') +PENDING = os.path.join(COURSE, 'pending') +SEARCH = 'https://webx11.duckdns.org/api/search' +OLLAMA = 'http://127.0.0.1:11434/v1/chat/completions' +MODEL = 'deepseek-v4.1-flash:cloud' +TOPICS = ['terminal UX best practices command design', 'browser virtual filesystem design', + 'module lazy loading cache busting web apps', 'Ollama local LLM tool calling best practices', + 'WebDAV client sync reliability', 'developer onboarding interactive tutorial design', + 'AI agent tool safety confirmation patterns', 'cron automation in single page apps', + 'privacy preserving product analytics', 'prompt engineering grounded answers no hallucination', + 'web terminal accessibility keyboard UX', 'self-hosted AI assistant cost control', + 'spaced repetition micro learning programming', 'secure API key handling in browser apps'] +SYS_DRAFT = ('You write ONE micro-lesson for the newOS4 terminal course. Return STRICT JSON only (no fences):\n' + '{"title":"...","minutes":4,"blocks":[{"type":"text|code|cmd|tip|warn","text":"..."}],' + '"exercise":{"instruction":"...","accept":["cmd ..."],"check":{"type":"manual"},"hint":"..."},"sources":["https://..."]}\n' + 'Rules: 3-5 blocks; at least one cmd block; every command MUST appear verbatim in the ALLOWED list; ' + 'only state facts present in the research snippets; sources must be URLs from the snippets.') + +def load(p, d): + try: + with open(p, encoding='utf-8') as f: return json.load(f) + except Exception: return d + +def save(p, obj): + tmp = p + '.tmp' + with open(tmp, 'w', encoding='utf-8') as f: json.dump(obj, f, ensure_ascii=False, indent=1) + os.replace(tmp, p); os.chmod(p, 0o644) + +def parse_json(txt): + txt = (txt or '').strip() + if txt.startswith('`' * 3): txt = txt.split('\n', 1)[1] if '\n' in txt else '' + txt = txt.strip() + if txt.endswith('`' * 3): txt = txt[:-3].strip() + m = re.search(r'\{[\s\S]*\}', txt) + try: return json.loads(m.group(0) if m else txt) + except Exception: return None + +def llm(system, user, n=900): + try: + r = requests.post(OLLAMA, json={'model': MODEL, 'stream': False, 'temperature': 0.4, 'max_tokens': n, + 'messages': [{'role': 'system', 'content': system}, {'role': 'user', 'content': user}]}, timeout=180) + if not r.ok: return None + return (r.json().get('choices') or [{}])[0].get('message', {}).get('content') + except Exception: + return None + +def research(q): + try: + r = requests.get(SEARCH, params={'q': q}, timeout=25) + if not r.ok: return [] + return [{'title': (x.get('title') or '')[:160], 'url': x['url'], 'snippet': (x.get('content') or '')[:600]} + for x in (r.json().get('results') or [])[:4] if x.get('url')] + except Exception: + return [] + +def validate_item(it, known, urls): + if not isinstance(it, dict): return False, 'not object' + if not (10 <= len(it.get('title') or '') <= 90): return False, 'title' + blocks = it.get('blocks') or [] + if not (3 <= len(blocks) <= 6): return False, 'block count' + if any(b.get('type') not in ('text', 'code', 'cmd', 'tip', 'warn', 'table') or not (b.get('text') or '').strip() for b in blocks): return False, 'block' + if not any(b.get('type') == 'cmd' for b in blocks): return False, 'no cmd' + ex = it.get('exercise') or {} + if not ex.get('instruction') or not ex.get('accept'): return False, 'exercise' + for a in ex['accept']: + if a.split(' ')[0] not in known: return False, 'unknown command ' + a.split(' ')[0] + if not any(str(s) in urls for s in (it.get('sources') or []) if str(s).startswith('http')): return False, 'no source' + return True, '' + +def main(): + dry = '--dry-run' in sys.argv + now = datetime.now(timezone.utc); day = now.strftime('%Y-%m-%d') + state = load(STATE, {'seen_commands': [], 'topic_index': 0, 'last_run': None}) + if not dry: subprocess.run([sys.executable, os.path.join(ROOT, 'scripts', 'gen-course-atlas.py')], check=True) + atlas = load(ATLAS, {}); known = set(atlas.get('commands', {})) + seen = set(state.get('seen_commands') or known) + new = sorted(known - seen) if state.get('seen_commands') else [] + feed = load(FEED, {'version': '', 'items': []}); added = []; rejected = [] + if new: + feed['items'].insert(0, {'id': 'new-' + day, 'date': day, 'title': f'{len(new)} new command(s) in newOS4', 'minutes': 2, + 'generated': False, 'sources': ['atlas.json'], + 'blocks': [{'type': 'text', 'text': 'Run `help <cmd>` for details.'}, {'type': 'cmd', 'text': '\n'.join(new[:12])}]}) + added.append('new-' + day) + if not any(i.get('id') == 'daily-' + day for i in feed['items']): + topic = TOPICS[state.get('topic_index', 0) % len(TOPICS)] + results = research(topic) + if results: + snippets = '\n'.join(f"- {r['title']} | {r['url']}\n {r['snippet']}" for r in results) + draft = parse_json(llm(SYS_DRAFT, f'TOPIC: {topic}\n\nSNIPPETS:\n{snippets}\n\nALLOWED COMMANDS:\n' + ', '.join(sorted(known)))) + ok, why = validate_item(draft, known, {r['url'] for r in results}) if draft else (False, 'no JSON') + if ok: + draft.update({'id': 'daily-' + day, 'date': day, 'generated': True}) + feed['items'].insert(0, draft); added.append('daily-' + day) + else: + os.makedirs(PENDING, exist_ok=True) + save(os.path.join(PENDING, f'daily-{day}.json'), {'draft': draft, 'why': why, 'topic': topic, 'results': results}) + rejected.append('daily: ' + why) + else: + rejected.append('daily: no research results') + state['topic_index'] = state.get('topic_index', 0) + 1 + feed['items'] = feed['items'][:60]; feed['version'] = day + if not dry: save(FEED, feed) + if not dry: subprocess.run([sys.executable, os.path.join(ROOT, 'scripts', 'course-insights.py')], check=True) + insights = load(os.path.join(COURSE, 'insights.json'), {}) + faq = load(FAQ, {'items': []}); have = {f.get('q') for f in faq['items']}; faq_n = 0 + for q in (insights.get('top_questions') or [])[:5]: + if q['q'] in have: continue + draft = parse_json(llm('Answer a repeated newOS4 question. Return STRICT JSON only: {"a":"<=400 chars","commands":["cmd"],"sources":["atlas.json#cmd"]}. Only use ALLOWED commands.', f'QUESTION: {q["q"]}\nALLOWED COMMANDS:\n' + ', '.join(sorted(known)), 500)) + if draft and draft.get('a') and all(c in known for c in draft.get('commands', [])): + faq['items'].append({'q': q['q'], 'a': draft['a'][:400], 'commands': draft['commands'], + 'sources': draft.get('sources') or ['insights.json'], 'added': day}) + have.add(q['q']); faq_n += 1 + else: rejected.append('faq: ' + q['q'][:60]) + if not dry: save(FAQ, faq) + state.update({'seen_commands': sorted(known), 'last_run': now.strftime('%Y-%m-%dT%H:%M:%SZ')}) + if not dry: + save(STATE, state) + with open(CHANGELOG, 'a', encoding='utf-8') as f: + f.write(json.dumps({'date': day, 'added': added, 'faq': faq_n, 'rejected': rejected, + 'atlas': f'{len(known)} commands'}, ensure_ascii=False) + '\n') + print('newOS4 Course daily — ' + day) + print('atlas: ' + str(len(known)) + ' commands | new: ' + str(len(new))) + print('daily lesson: ' + next((i['title'] for i in feed['items'] if i.get('id') == 'daily-' + day), 'none added')) + print('faq added: ' + str(faq_n) + ' | feed: ' + str(len(feed['items'])) + '/60') + print('rejected: ' + ('; '.join(rejected) if rejected else 'none')) + print('needs OK: none') + +if __name__ == '__main__': + sys.exit(main() or 0) diff --git a/scripts/course-insights.py b/scripts/course-insights.py new file mode 100644 index 0000000..0ee891e --- /dev/null +++ b/scripts/course-insights.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +"""Aggregate course events -> course/insights.json (counts + capped question text, no sids/IPs).""" +import json, os, glob, re +from collections import Counter +from datetime import datetime, timezone + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +DATA = os.path.join(ROOT, 'data', 'course') +OUT = os.path.join(ROOT, 'course', 'insights.json') + +def norm_q(q): + q = re.sub(r'https?://\S+', '', q or '') + return re.sub(r'\s+', ' ', q).strip().lower()[:200] + +def main(): + opens, completes, fails, cmds, questions = Counter(), Counter(), Counter(), Counter(), Counter() + projects = Counter() + files = sorted(glob.glob(os.path.join(DATA, 'events-*.jsonl')))[-2:] + for path in files: + with open(path, encoding='utf-8', errors='ignore') as f: + for line in f: + try: e = json.loads(line) + except Exception: continue + t, d = e.get('type'), e.get('detail') or {} + lesson = (d.get('lesson') or '')[:40] + if t == 'lesson_open' and lesson: opens[lesson] += 1 + elif t == 'lesson_complete' and lesson: completes[lesson] += 1 + elif t == 'exercise_fail': + if lesson: fails[lesson] += 1 + c = (d.get('command') or '').strip().split(' ')[0][:40] + if c: cmds[c] += 1 + elif t == 'question': + q = norm_q(d.get('q')) + if len(q) >= 8: questions[q] += 1 + elif t in ('project_open', 'project_view'): + p = (d.get('project') or '')[:40] + if p: projects[p] += 1 + lessons = {} + for lid in set(opens) | set(completes): + o, c = opens[lid], completes[lid] + lessons[lid] = {'opens': o, 'completes': c, 'failures': fails.get(lid, 0), + 'completion_rate': round(c / o, 2) if o else 0.0} + payload = {'generated': datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ'), + 'window_files': [os.path.basename(p) for p in files], + 'lessons': dict(sorted(lessons.items())), + 'top_failed_commands': [{'cmd': c, 'n': n} for c, n in cmds.most_common(20)], + 'top_questions': [{'q': q, 'n': n} for q, n in questions.most_common(20) if n >= 2], + 'projects': dict(projects), + 'totals': {'opens': sum(opens.values()), 'completes': sum(completes.values()), 'failures': sum(fails.values())}} + os.makedirs(os.path.dirname(OUT), exist_ok=True) + tmp = OUT + '.tmp' + with open(tmp, 'w', encoding='utf-8') as f: json.dump(payload, f, ensure_ascii=False, indent=1) + os.replace(tmp, OUT); os.chmod(OUT, 0o644) + print(json.dumps({'lessons': len(lessons), 'questions': len(payload['top_questions']), 'totals': payload['totals']})) + +if __name__ == '__main__': + main() diff --git a/scripts/course-validate.py b/scripts/course-validate.py new file mode 100644 index 0000000..9b5dcf4 --- /dev/null +++ b/scripts/course-validate.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +"""Validate newOS4 course content. Exit 0 = publishable, 1 = errors printed.""" +import json, os, glob, sys + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +COURSE = os.path.join(ROOT, 'course') +ATLAS = os.path.join(COURSE, 'atlas.json') +errors, warnings = [], [] + +def load(p): + try: + with open(p, encoding='utf-8') as f: + return json.load(f) + except Exception as e: + errors.append(f'{os.path.relpath(p, ROOT)}: invalid JSON: {e}') + return None + +def main(): + manifest = load(os.path.join(COURSE, 'course.json')) + if not manifest: + return finish() + ids, lesson_ids = set(), set() + for ch in manifest.get('chapters', []): + cid = ch.get('id') + if not cid: + errors.append('course.json: chapter missing id'); continue + if cid in ids: + errors.append(f'course.json: duplicate chapter id {cid}') + ids.add(cid) + path = os.path.join(COURSE, 'chapters', cid + '.json') + if not os.path.exists(path): + errors.append(f'missing chapter file {cid}.json'); continue + chd = load(path) + if not chd: continue + man_ids = [x.get('id') for x in ch.get('lessons', [])] + file_ids = [x.get('id') for x in chd.get('lessons', [])] + if man_ids != file_ids: + errors.append(f'{cid}: manifest lessons {man_ids} != chapter file lessons {file_ids}') + for l in chd.get('lessons', []): + lid = l.get('id') + if not lid: + errors.append(f'{cid}: lesson missing id'); continue + if lid in lesson_ids: + errors.append(f'duplicate lesson id {lid}') + lesson_ids.add(lid) + for k in ('title', 'objective'): + if not l.get(k): errors.append(f'{lid}: missing {k}') + mins = l.get('minutes', 0) + if not isinstance(mins, int) or isinstance(mins, bool) or mins < 1: + errors.append(f'{lid}: minutes must be >=1') + blocks = l.get('blocks') or [] + if len(blocks) < 3: errors.append(f'{lid}: needs >=3 blocks') + for b in blocks: + if b.get('type') not in ('text', 'code', 'cmd', 'tip', 'warn', 'table'): + errors.append(f'{lid}: bad block type {b.get("type")!r}') + ex = l.get('exercise') + if ex: + if not ex.get('instruction') or not ex.get('accept'): + errors.append(f'{lid}: exercise needs instruction+accept') + chk = ex.get('check', {}) + if chk.get('type') not in ('command', 'file', 'output', 'manual'): + errors.append(f'{lid}: exercise check.type invalid') + if chk.get('type') == 'file' and not chk.get('path'): + errors.append(f'{lid}: file check requires path') + if chk.get('type') == 'output' and not chk.get('contains'): + errors.append(f'{lid}: output check requires contains') + if not ex.get('hint'): warnings.append(f'{lid}: exercise without hint') + if l.get('generated') and not l.get('sources'): + errors.append(f'{lid}: generated lesson needs sources') + if os.path.exists(ATLAS): + atlas = load(ATLAS) or {} + known = set(atlas.get('commands', {})) + for f in glob.glob(os.path.join(COURSE, 'chapters', '*.json')): + chd = load(f) or {} + for l in chd.get('lessons', []): + for c in l.get('commands', []): + if c not in known: + errors.append(f'{l.get("id")}: unknown command {c}') + for name, key in (('feed.json', 'items'), ('faq.json', 'items')): + p = os.path.join(COURSE, name) + if not os.path.exists(p): continue + d = load(p) or {} + for it in d.get(key, []): + if isinstance(it, dict) and it.get('date') is None: + warnings.append(f'{name}: item without date') + return finish() + +def finish(): + for w in warnings: print(f'WARN {w}') + for e in errors: print(f'ERROR {e}') + print(f'course-validate: {len(errors)} error(s), {len(warnings)} warning(s)') + return 1 if errors else 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/scripts/gen-course-atlas.py b/scripts/gen-course-atlas.py new file mode 100644 index 0000000..559fd4c --- /dev/null +++ b/scripts/gen-course-atlas.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +"""Build course/atlas.json from newOS4 source. Ground truth for every lesson claim.""" +import json, os, re, glob, hashlib +from datetime import datetime, timezone + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +OUT = os.path.join(ROOT, 'course', 'atlas.json') +CORE_DIR, EXT_DIR = os.path.join(ROOT, 'modules', 'core'), os.path.join(ROOT, 'modules', 'ext') +SKIP = {'core-bundle.js', 'critical-ext-bundle.js', 'sandbox-worker.js'} + +CATEGORY = [ + ('module-fs', 'filesystem'), ('virtual-fs', 'filesystem'), ('module-aiassist', 'ai'), ('module-ollama', 'ai'), + ('module-memory-graph', 'ai'), ('module-smartctx', 'ai'), ('module-neural-vfs', 'ai'), ('module-world-model', 'ai'), + ('module-sandbox', 'ai'), ('module-searxng', 'web'), ('module-fetcher', 'web'), ('webdav', 'cloud'), + ('editor', 'editor'), ('filemgr', 'filesystem'), ('finder', 'filesystem'), ('diff', 'editor'), + ('cron', 'automation'), ('kanban', 'automation'), ('shell', 'shell'), ('busybox', 'shell'), + ('theme', 'ui'), ('floating', 'ui'), ('voice', 'ui'), ('three', 'ui'), ('console', 'dev'), ('debug-panel', 'dev'), + ('wpm', 'system'), ('tutorial', 'learning'), ('skill-pack-core', 'ai'), ('cryptogzip', 'system'), + ('module-agent-registry', 'agents'), ('module-orchestrator', 'agents'), ('module-leases', 'agents'), + ('module-chatstudio', 'ai'), ('module-servermon', 'system'), ('module-config', 'system'), ('module-system', 'system'), + ('course', 'learning'), +] + +def category_for(filename, cmd): + for frag, cat in CATEGORY: + if frag in filename: return cat + if cmd.startswith('webdav'): return 'cloud' + if cmd.startswith('cron') or cmd.startswith('kanban'): return 'automation' + if cmd.startswith('ai-') or cmd in ('ai', 'ask'): return 'ai' + return 'general' + +def tier_lists(): + rebuild = open(os.path.join(ROOT, 'scripts', 'rebuild-bundles.py')).read() + def listed(name): + m = re.search(name + r"\s*=\s*\[([^\]]+)\]", rebuild, re.S) + return set(re.findall(r"([a-z0-9-]+\.js)", m.group(1))) if m else set() + loader = open(os.path.join(ROOT, 'modules', 'core', 'module-loader.js')).read() + m = re.search(r'extKeys\s*=\s*\[([^\]]+)\]', loader) + ext = set(re.findall(r"'([a-z0-9-]+)'", m.group(1))) if m else set() + return listed('CORE_FILES'), listed('CRITICAL_EXT_FILES'), ext + +CORE_FILES, CRITICAL_EXT_FILES, EXT_KEYS = tier_lists() + +def module_tier(filename): + base = os.path.basename(filename) + if base in CRITICAL_EXT_FILES: return 'critical-ext-bundle' + if base in CORE_FILES: return 'core-bundle' + if base[:-3] in EXT_KEYS: return 'ext-auto' + return 'ext-manual' + +def desc_map(text): + out = {} + for m in re.finditer(r"'([a-z0-9][a-z0-9_-]{1,40})'\s*:\s*'((?:[^'\\]|\\.){8,300})'", text): + cmd, desc = m.group(1), m.group(2) + if len(desc) < 10 or ' ' not in desc or desc.endswith('.js'): + continue + out.setdefault(cmd, desc.replace("\\'", "'")) + return out + +def main(): + commands, modules = {}, {} + files = sorted(glob.glob(os.path.join(CORE_DIR, '*.js')) + glob.glob(os.path.join(EXT_DIR, '*.js'))) + for path in files: + base = os.path.basename(path) + if base in SKIP: continue + text = open(path, encoding='utf-8', errors='ignore').read() + cmds = sorted(set(re.findall(r"commands\['([A-Za-z0-9_-]+)'\]\s*=", text))) + if not cmds: continue + modname = base[:-3] + modules[modname] = {'file': os.path.relpath(path, ROOT), 'tier': module_tier(path), + 'commands': cmds, 'lines': text.count('\n') + 1} + dm = desc_map(text) + for c in cmds: + commands[c] = {'module': modname, 'file': os.path.relpath(path, ROOT), 'tier': module_tier(path), + 'category': category_for(base, c), 'desc': dm.get(c, ''), + 'builtin': False, 'example': c} + term = open(os.path.join(ROOT, 'modules', 'core', 'terminal.js'), encoding='utf-8').read() + for c in sorted(set(re.findall(r"cmd === '([a-z0-9-]+)'", term))): + if c in commands: continue + commands[c] = {'module': 'terminal', 'file': 'modules/core/terminal.js', 'tier': 'core-bundle', + 'category': 'system', 'desc': '', 'builtin': True, 'example': c} + idx_path = os.path.join(ROOT, 'modules-index.json') + mod_by_cmd = {} + if os.path.exists(idx_path): + idx = json.load(open(idx_path)) + for m in idx.get('modules', {}).values(): + for c in m.get('commands', []): + mod_by_cmd.setdefault(c, m) + for c, e in commands.items(): + if not e['desc']: + m = mod_by_cmd.get(c) or {} + e['desc'] = (m.get('description') or f'{c} — command from {e["module"]}')[:300] + payload = { + 'version': datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ'), + 'source_hash': hashlib.sha256(''.join(sorted(commands)).encode()).hexdigest()[:16], + 'commands': dict(sorted(commands.items())), + 'modules': dict(sorted(modules.items())), + 'coverage': {'commands': len(commands), + 'described': sum(1 for e in commands.values() if e['desc']), + 'modules': len(modules), + 'tiers': {t: sum(1 for e in commands.values() if e['tier'] == t) + for t in ('core-bundle', 'critical-ext-bundle', 'ext-auto', 'ext-manual')}}, + } + os.makedirs(os.path.dirname(OUT), exist_ok=True) + tmp = OUT + '.tmp' + with open(tmp, 'w', encoding='utf-8') as f: + json.dump(payload, f, ensure_ascii=False, indent=1) + os.replace(tmp, OUT) + os.chmod(OUT, 0o644) + print(json.dumps(payload['coverage'], ensure_ascii=False)) + +if __name__ == '__main__': + main() diff --git a/scripts/newos4-maintain.py b/scripts/newos4-maintain.py new file mode 100644 index 0000000..6321a3f --- /dev/null +++ b/scripts/newos4-maintain.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +"""newOS4 maintenance: drift, bundles, syntax, perms, data hygiene, live health. +Safe fixes only with --fix. Never touches nginx, services, sudo, or user content.""" +import glob, json, os, re, subprocess, sys +from datetime import datetime, timezone + +ROOT = '/var/www/webx11.duckdns.org/spaces/newos4' +WEB = 'https://webx11.duckdns.org/spaces/newos4' +FIX = '--fix' in sys.argv +DEEP = '--deep' in sys.argv +issues = [] +def add(l, m): issues.append((l, m)) +def run(cmd, **kw): return subprocess.run(cmd, capture_output=True, text=True, **kw) +def sh(cmd): return run(cmd, shell=True).stdout.strip() + +def check_versions(): + html = open(os.path.join(ROOT, 'main.html'), encoding='utf-8').read() + loader = open(os.path.join(ROOT, 'modules/core/module-loader.js'), encoding='utf-8').read() + m = re.search(r"const LATEST_VER\s*=\s*'([^']+)'", loader) + ver = m.group(1) if m else '?' + n = html.count(ver) + add('PASS' if n == 3 else 'FAIL', f'version {ver} x{n} in main.html (expect 3)') + +def check_bundles(): + paths = ('modules/core/core-bundle.js', 'modules/ext/critical-ext-bundle.js') + before = {p: sh(f'md5sum {p}') for p in paths} + if not FIX: + add('PASS', 'bundle check skipped (use --fix)'); return + r = run([sys.executable, 'scripts/rebuild-bundles.py'], cwd=ROOT) + after = {p: sh(f'md5sum {p}') for p in paths} + changed = [p for p in paths if before[p] != after[p]] + add('FAIL' if r.returncode else ('WARN' if changed else 'PASS'), + 'bundles ' + ('FAILED' if r.returncode else ('rebuilt: ' + ', '.join(changed) if changed else 'clean'))) + +def check_syntax(): + bad = [] + for p in glob.glob(os.path.join(ROOT, 'modules/core/*.js')) + glob.glob(os.path.join(ROOT, 'modules/ext/*.js')): + if os.path.basename(p) in ('core-bundle.js', 'critical-ext-bundle.js'): continue + if run(['node', '--check', p]).returncode: bad.append(os.path.basename(p)) + add('PASS' if not bad else 'FAIL', 'syntax ' + ('OK' if not bad else 'fail: ' + ', '.join(bad))) + +def check_perms(): + targets = glob.glob(os.path.join(ROOT, 'course/*.json')) + glob.glob(os.path.join(ROOT, 'course/chapters/*.json')) \ + + [os.path.join(ROOT, 'modules/ext/course.js'), os.path.join(ROOT, 'course.html'), os.path.join(ROOT, 'api/course/event.php')] + bad = [] + for p in targets: + if not os.path.exists(p): continue + if (os.stat(p).st_mode & 0o777) != 0o644 or sh(f'stat -c %G {p}') != 'www-data': + if FIX: + os.chmod(p, 0o644); sh(f'chgrp www-data {p}') + else: bad.append(os.path.basename(p)) + add('PASS' if not bad else ('WARN' if FIX else 'FAIL'), 'perms ' + ('clean' if not bad else ('fixed' if FIX else 'bad: ' + ', '.join(bad[:5])))) + +def check_index(): + idxp = os.path.join(ROOT, 'modules-index.json') + atlas = json.load(open(os.path.join(ROOT, 'course/atlas.json'))) + idx = json.load(open(idxp)) + cur = len(set(c for m in idx.get('modules', {}).values() for c in m.get('commands', []))) + if cur == atlas['coverage']['commands']: + add('PASS', f'modules-index {cur} commands'); return + add('WARN', f'modules-index stale {cur} vs {atlas["coverage"]["commands"]}') + if not FIX: return + for name, m in atlas['modules'].items(): + e = idx['modules'].get(name, {'name': name, 'file': m['file'], 'type': 'core' if m['tier'] == 'core-bundle' else 'ext', + 'class': '', 'auto_load': m['tier'] != 'ext-manual', 'description': ''}) + e.update({'commands': m['commands'], 'file': m['file'], 'tier': m['tier']}) + idx['modules'][name] = e + idx['version'] = datetime.now(timezone.utc).strftime('%Y-%m-%d') + '-atlas' + tmp = idxp + '.tmp' + with open(tmp, 'w', encoding='utf-8') as f: json.dump(idx, f, ensure_ascii=False, indent=1) + os.replace(tmp, idxp); os.chmod(idxp, 0o644); sh(f'chgrp www-data {idxp}') + add('PASS', 'modules-index regenerated') + +def check_data(): + err = os.path.join(ROOT, 'data/debug/errors.json') + if os.path.exists(err) and os.path.getsize(err) > 5 * 1024 * 1024: + if FIX: + arc = os.path.join(ROOT, 'data/debug/archive'); os.makedirs(arc, exist_ok=True) + os.replace(err, os.path.join(arc, 'errors-' + datetime.now().strftime('%Y%m') + '.json')) + open(err, 'w').write('[]'); os.chmod(err, 0o664); sh(f'chgrp www-data {err}') + add('PASS', 'errors.json rotated') + else: add('WARN', 'errors.json >5MB') + else: add('PASS', 'debug data OK') + +def check_live(): + for url in (f'{WEB}/main.html', f'{WEB}/course.html', f'{WEB}/course/course.json', f'{WEB}/modules/core/core-bundle.js'): + code = sh(f"curl -s -o /dev/null -w '%{{http_code}}' '{url}'") + add('PASS' if code == '200' else 'FAIL', f'HTTP {code} {url.replace(WEB, "")}') + +def check_course(): + r = run([sys.executable, 'scripts/course-validate.py'], cwd=ROOT) + add('PASS' if r.returncode == 0 else 'FAIL', 'course ' + ('valid' if r.returncode == 0 else 'INVALID: ' + r.stdout[-160:])) + +def check_links(): + if not DEEP: return + broken = [] + for page in ('main.html', 'course.html', 'help.html', 'modules.html', 'guide.html'): + p = os.path.join(ROOT, page) + if not os.path.exists(p): continue + for href in re.findall(r'(?:href|src)="([^"#]+)"', open(p, encoding='utf-8').read()): + if any(x in href for x in ("'", '+', '{', '}')): continue + if href.startswith(('http', '//', 'mailto:', 'javascript:', 'data:', '/api/')): continue + target = os.path.normpath(os.path.join(ROOT, href.split('?')[0])) + if not os.path.exists(target): broken.append(f'{page}:{href}') + add('PASS' if not broken else 'WARN', 'links ' + ('OK' if not broken else f'{len(broken)} broken (first: {broken[0]})')) + +def main(): + os.chdir(ROOT) + check_versions(); check_bundles(); check_syntax(); check_perms(); check_index(); check_data(); check_live(); check_course(); check_links() + fails = [m for l, m in issues if l == 'FAIL'] + print('newOS4 maintenance — ' + datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%MZ') + (' [fix]' if FIX else '') + (' [deep]' if DEEP else '')) + for l, m in issues: print(f'{l}: {m}') + print('maintenance result: ' + ('FAIL' if fails else 'OK')) + print('needs OK: ' + ('none' if not fails else '; '.join(fails)[:400])) + return 1 if fails else 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/scripts/test-course.mjs b/scripts/test-course.mjs new file mode 100644 index 0000000..7a64dc8 --- /dev/null +++ b/scripts/test-course.mjs @@ -0,0 +1,59 @@ +import fs from 'fs'; +import vm from 'vm'; +const SRC = '/var/www/webx11.duckdns.org/spaces/newos4/modules/ext/course.js'; +const MANIFEST = { version: 'test-1', updated: '2026-10-18', chapters: [ + { id: '01-orientation', title: 'Orientation', lessons: [{ id: '1.1', title: 'T', objective: 'O', minutes: 5 }] } +], projects: ['p1'] }; +const CHAPTER = { id: '01-orientation', title: 'Orientation', lessons: [{ id: '1.1', title: 'T', objective: 'O', minutes: 5, + blocks: [{ type: 'text', text: 'hello' }, { type: 'cmd', text: 'status' }, { type: 'tip', text: 'tip' }], + exercise: { instruction: 'touch ok.txt', accept: ['touch ok.txt'], check: { type: 'file', path: '/ok.txt' }, hint: 'touch ok.txt' } }] }; +let printed = []; +const terminal = { + print(t) { printed.push(String(t)); }, + fs: { _f: {}, readFile(p) { return Object.prototype.hasOwnProperty.call(this._f, p) ? this._f[p] : null; }, + writeFile(p, c) { this._f[p] = c; }, mkdirRecursive() {}, fsReadJSON(p) { try { return JSON.parse(this._f[p] || 'null'); } catch (e) { return null; } }, + fsWriteJSON(p, o) { this._f[p] = JSON.stringify(o); } }, + _pushPrintCapture(cb) { this._cap = cb; }, _popPrintCapture() { this._cap && this._cap('output'); }, + ollamaManager: { currentModel: 'test-model' } +}; +const store = {}; +const sandbox = { + window: { webx11: { moduleManager: { registerModule() {} } } }, + localStorage: { getItem: k => store[k] ?? null, setItem: (k, v) => { store[k] = String(v); }, removeItem: k => { delete store[k]; } }, + fetch: async (url) => { + if (url.includes('course.json')) return { ok: true, json: async () => MANIFEST }; + if (url.includes('atlas.json')) return { ok: true, json: async () => ({ commands: { status: { desc: 'Status', module: 'system', tier: 'core-bundle', category: 'system' }, touch: { desc: 'Touch', module: 'fs', tier: 'core-bundle', category: 'filesystem' }, ls: { desc: 'List', module: 'fs', tier: 'core-bundle', category: 'filesystem' } } }) }; + if (url.includes('chapters/')) return { ok: true, json: async () => CHAPTER }; + if (url.includes('event.php')) return { ok: true, json: async () => ({}) }; + if (url.includes('/ai/')) return { ok: true, json: async () => ({ choices: [{ message: { content: 'answer' } }] }) }; + return { ok: false, status: 404, json: async () => ({}) }; + }, + console, document: { body: {} }, setTimeout, clearTimeout, Date, Math, JSON, RegExp +}; +sandbox.globalThis = sandbox; +sandbox.webx11 = sandbox.window.webx11; +vm.createContext(sandbox); +vm.runInContext(fs.readFileSync(SRC, 'utf8') + '\n;globalThis.__CourseModule = CourseModule;', sandbox); +const mod = new sandbox.__CourseModule(); +mod.terminal = terminal; +let pass = 0, fail = 0; +const check = (n, c, got) => { if (c) { pass++; console.log('PASS', n); } else { fail++; console.log('FAIL', n, got || ''); } }; +const run = async () => { + mod.registerCommands(); + await mod._warm(); + await mod.handle(['list']); + check('list mentions Orientation', printed.join('\n').includes('Orientation')); + await mod.handle(['open', '1.1']); + check('lesson printed', printed.join('\n').includes('Exercise')); + mod.onCommandStart('touch', ['ok.txt']); terminal.fs.writeFile('/ok.txt', 'x'); mod.onCommandExecuted('touch', ['ok.txt']); + check('file exercise completes', !!mod._progress().completed['1.1'], JSON.stringify(mod._progress())); + check('xp awarded', mod._progress().xp >= 10); + check('match exact', mod._match('ls', 'ls') && !mod._match('ls', 'ls -la')); + check('match wildcard', mod._match('touch *', 'touch a.txt')); + await mod.handle(['ask', 'what is status?']); + check('tutor answered', printed.join('\n').includes('answer')); + check('skill installed', !!terminal.fs._f['/skills/course-01-orientation.md']); + console.log(`RESULT ${pass} passed, ${fail} failed`); + process.exit(fail ? 1 : 0); +}; +run().catch(e => { console.error(e); process.exit(1); }); |