diff options
27 files changed, 2278 insertions, 575 deletions
diff --git a/A2A_protocol_spec.md b/A2A_protocol_spec.md new file mode 100644 index 0000000..e73b0e9 --- /dev/null +++ b/A2A_protocol_spec.md @@ -0,0 +1,45 @@ +# A2A Protocol Specification v0.1 + +## Overview +Agent-to-Agent (A2A) is a decentralized message protocol for agent negotiation within newOS4. Agents discover each other via VFS registrations and communicate through a shared message queue. No central broker. + +## Message Format +All messages are JSON with this envelope: + +```json +{ + "a2a_version": "0.1", + "msg_id": "<uuid>", + "type": "request" | "response" | "broadcast" | "lease" | "heartbeat", + "from": "<agent_id>", + "to": "<agent_id>" | "*", + "timestamp": "<ISO 8601>", + "ttl_seconds": 30, + "contract": { + "capabilities": ["list_files", "read_file", "web_search"], + "constraints": { + "max_tool_turns": 8, + "max_duration_seconds": 60 + } + }, + "payload": { } +} +``` + +## Transport Layer +Each agent has a VFS mailbox at `/agents/<agent_id>/inbox.json` and publishes to `/agents/<agent_id>/outbox.json`. The orchestrator monitors inboxes via periodic polling (500ms interval). + +## Message Types +- **request**: Point-to-point task delegation. From=orchestrator, To=worker. +- **response**: Task result. From=worker, To=orchestrator. +- **broadcast**: Discovery / capability advertisement. To="*". +- **lease**: Time-bound contract granting a worker exclusive task rights. +- **heartbeat**: Alive signal. Every 5 seconds. Missing 3 consecutive = presumed dead. + +## Lifecycle +1. Orchestrator broadcasts AGENT_REQUEST +2. Workers respond with capability claim +3. Orchestrator issues LEASE with TTL +4. Worker executes, sends RESPONSE +5. Orchestrator ACKs, LEASE expires +6. Worker returns to available pool
\ No newline at end of file diff --git a/BUGFIX-SUGGESTIONS.md b/BUGFIX-SUGGESTIONS.md deleted file mode 100644 index 1d10fb6..0000000 --- a/BUGFIX-SUGGESTIONS.md +++ /dev/null @@ -1,355 +0,0 @@ -# newOS4 Bug Fix Suggestions - -**Generated:** 2026-05-24 -**Status:** Pending Review -**Priority:** High (13 issues across 7 files) - ---- - -## Executive Summary - -| Category | Issues | Files | Severity | -|----------|--------|-------|----------| -| File Structure | 5 | 4 | High | -| Agent System | 5 | 2 | High | -| AI Routing | 2 | 1 | Medium | -| Cache | 1 | 1 | Low | -| **Total** | **13** | **7** | | - ---- - -## 1. File Structure Issues - -### 1.1 Splash Fallback Ext List Incomplete -- **File:** `index.html` -- **Line:** 770 -- **Issue:** Splash fallback hardcodes 5 ext modules but `modules/ext/` directory has 8 files -- **Missing:** `mindmap.js`, `webdav.js`, `skill-pack-core.js` -- **Fix:** - ```javascript - // Before (5 modules) - ['Extensions', ['calc.js','voice.js','cron.js','theme.js','wpm.js'], false] - - // After (8 modules) - ['Extensions', ['calc.js','voice.js','cron.js','theme.js','wpm.js','mindmap.js','webdav.js','skill-pack-core.js'], false] - ``` - ---- - -### 1.2 Preview Iframe Blocks JavaScript -- **File:** `index.html` -- **Line:** 684 -- **Issue:** `<iframe sandbox="allow-same-origin">` missing `allow-scripts` -- **Impact:** HTML previews with JavaScript don't execute -- **Fix:** - ```html - <!-- Before --> - <iframe sandbox="allow-same-origin"></iframe> - - <!-- After --> - <iframe sandbox="allow-same-origin allow-scripts"></iframe> - ``` - ---- - -### 1.3 Dead Code: EXT_FILES Array -- **File:** `module-loader.js` -- **Lines:** 35-37 -- **Issue:** `EXT_FILES` array defined but never referenced (dead code, wrong count) -- **Impact:** Maintenance confusion, wrong module count (6 vs actual 8) -- **Fix:** Remove entirely — ext loading uses separate `extKeys` array on line 159 - ---- - -### 1.4 Packages.json URL Errors -- **File:** `packages.json` -- **Lines:** 7-167 -- **Issues:** - - All 18 package URLs point to `modules/<name>.js` (wrong path) - - Actual files are in `modules/ext/<name>.js` - - 12 packages reference files that don't exist anywhere -- **Impact:** WPM installs fail with 404 errors -- **Fix Options:** - 1. **Clean:** Remove 12 phantom packages, fix 6 real URLs to `modules/ext/` - 2. **Stub:** Create missing files in `modules/ext/` -- **Recommendation:** Option 1 (clean) — reduces bloat - ---- - -### 1.5 Search Uses Intrusive Alert() -- **File:** `help.html` -- **Line:** 1149 -- **Issue:** `alert('No results found')` blocks UI -- **Impact:** Poor UX, modal interruption -- **Fix:** - ```javascript - // Before - if (!found) alert('No results found for: ' + query); - - // After - if (!found) { - const msg = document.getElementById('search-result-msg'); - if (msg) { - msg.textContent = 'No results found for: ' + query; - msg.style.display = 'block'; - setTimeout(() => { msg.style.display = 'none'; }, 3000); - } - } - ``` - ---- - -## 2. Agent System Issues - -### 2.1 Agent Loop Caps at 1 Turn (Not 5) -- **File:** `module-aiassist.js` -- **Lines:** 537-547 -- **Issue:** When model returns text + tool_calls, agent executes tools then `return`s (exits loop) -- **Impact:** Agent stops after 1 tool turn instead of advertised `MAX_TOOL_TURNS = 5` -- **Root Cause:** Follow-up text-response block returns instead of continuing loop -- **Fix:** - ```javascript - // Before (lines 537-548) - if (pendingText) { - const followUp = await this.terminal.ollamaManager.chatWithTools(messages, []); - if (followUp.message && followUp.message.content) { - this.terminal.printAIResponse(followUp.message.content); - return { finalAnswer: followUp.message.content, toolTurns }; // ← exits loop - } - } - continue; - - // After - if (pendingText) { - messages.push({ role: 'assistant', content: pendingText }); - } - continue; // ← loop continues, allows up to 5 turns - ``` - ---- - -### 2.2 edit_module Not Checked as Destructive -- **File:** `module-aiassist.js` -- **Line:** 730 -- **Issue:** `isDestructive()` doesn't include `edit_module` but system prompt (line 227) says it's `(DESTRUCTIVE)` -- **Impact:** AI told to ask permission but code never confirms -- **Fix:** - ```javascript - // Before - isDestructive(name) { - return name === 'write_file' || name === 'delete_file' || - name === 'run_command' || name === 'make_directory'; - } - - // After - isDestructive(name) { - return name === 'write_file' || name === 'delete_file' || - name === 'run_command' || name === 'make_directory' || - name === 'edit_module'; - } - ``` - ---- - -### 2.3 Empty tools:[] Sent to Ollama -- **File:** `module-aiassist.js` -- **Line:** 539 -- **Issue:** `chatWithTools(messages, [])` sends empty tools array -- **Impact:** Some Ollama models reject `tools: []`, triggers unnecessary fallback request -- **Fix:** Remove follow-up call; push text as `assistant` message then `continue` loop (see 2.1) - ---- - -### 2.4 Default Model Blocks Auto-Selection -- **File:** `ollama-manager.js` -- **Lines:** 8, 42-45 -- **Issue:** Default model `'gemma4:31b-cloud'` is truthy, so auto-selection skipped even if model doesn't exist -- **Impact:** Users get silent model errors until manually selecting -- **Fix:** - ```javascript - // Before (lines 42-45) - if (!this.currentModel && this.models.length > 0) { - this.currentModel = this.models[0].name; - // ... - } - - // After - if (!this.currentModel || !this.models.some(m => m.name === this.currentModel)) { - if (this.models.length > 0) { - this.currentModel = this.models[0].name; - // ... - } - } - ``` - ---- - -### 2.5 abort() Misses chat() and chatStream() -- **File:** `ollama-manager.js` -- **Lines:** 70, 140, 176-183 -- **Issue:** `_abortController` only set in `chatWithTools()` (line 195), not in `chat()` or `chatStream()` -- **Impact:** `ai-stop` command only cancels tool-calling requests, not direct chat or streaming -- **Fix:** - ```javascript - // Add to chat() method (around line 70) - this._abortController = new AbortController(); - const signal = this._abortController.signal; - const response = await fetch(`${this.endpoint}/api/generate`, { - // ... - signal - }); - this._abortController = null; - - // Add to chatStream() method (around line 140) - same pattern - ``` - ---- - -## 3. AI Routing Issues - -### 3.1 Unknown Commands Not Awaited (executeCommand) -- **File:** `terminal.js` -- **Line:** 629 -- **Issue:** `aiMod.chat([rawInput.trim()])` called without `await` -- **Impact:** Fire-and-forget execution, errors uncaught, no sequencing -- **Fix:** - ```javascript - // Before - aiMod.chat([rawInput.trim()]); - return; - - // After - this.print('→ Routing to AI assistant...', 'info'); - await aiMod.chat([rawInput.trim()]); - return; - ``` - ---- - -### 3.2 Unknown Commands Not Awaited (dispatchCommand) -- **File:** `terminal.js` -- **Line:** 691 -- **Issue:** Same as 3.1 but in `dispatchCommand()` (used for pipe chains) -- **Fix:** Same as 3.1 - -**Combined Impact:** Unknown commands like `hello` route to AI but without proper error handling or user feedback - ---- - -## 4. Cache Invalidation - -### 4.1 Cache Version Stale -- **File:** `module-loader.js` -- **Line:** 102 -- **Issue:** `CACHE = '?v=17'` serves old cached modules -- **Impact:** Browser doesn't re-fetch updated module files -- **Fix:** - ```javascript - // Before - const CACHE = '?v=17'; - - // After - const CACHE = '?v=18'; - ``` -- **Note:** Requires users to hard refresh (Ctrl+F5 / Cmd+Shift+R) - ---- - -## Implementation Priority - -### High Priority (Critical Bugs) -1. **2.1** Agent loop caps at 1 turn — breaks multi-step AI tasks -2. **1.4** Packages.json 404 errors — WPM installs broken -3. **2.4** Default model blocks auto-selection — invisible failures -4. **2.5** abort() incomplete — `ai-stop` doesn't work for chat/stream - -### Medium Priority (Functional Issues) -5. **1.1** Splash fallback incomplete — minor UX issue -6. **2.2** edit_module destructive check — security inconsistency -7. **3.1 + 3.2** AI routing without await — error handling gaps -8. **1.2** Preview iframe sandbox — JS previews broken - -### Low Priority (Cleanup) -9. **1.3** Dead EXT_FILES code — maintenance clarity -10. **1.5** Search alert() → inline — UX polish -11. **4.1** Cache version — required after applying fixes - ---- - -## Tradeoffs & Considerations - -### Packages.json Cleanup (1.4) -- **Option A (Recommended):** Remove 12 phantom packages - - ✅ Reduces bloat, cleaner codebase - - ❌ Breaks backward compatibility if anyone installed them -- **Option B:** Create stub files for missing packages - - ✅ Preserves compatibility - - ❌ Adds dead weight, maintenance burden - -### Agent Loop Change (2.1) -- Removing follow-up call changes message structure slightly -- Text+tool hybrid responses now push text as `assistant` message, then loop continues -- This is correct behavior but differs from original design - -### AI Routing (3.1 + 3.2) -- Routing all unknown commands to AI may increase token usage -- Consider adding `ai-mode` toggle or `--no-ai` flag for opt-out - -### Cache Version (4.1) -- Bumping version requires all users to hard refresh -- Consider adding release notes or announcement - ---- - -## Testing Checklist - -After applying fixes: - -- [ ] Hard refresh page (Ctrl+F5) -- [ ] Test splash screen shows all 8 ext modules -- [ ] Test HTML preview with JavaScript executes -- [ ] Test WPM package installs succeed -- [ ] Test search shows inline message (no alert) -- [ ] Test multi-step AI task (5 tool turns) -- [ ] Test `edit_module` prompts for confirmation -- [ ] Test auto-select model on connect -- [ ] Test `ai-stop` cancels chat/stream -- [ ] Test unknown command routes to AI (`hello`) - ---- - -## Files to Modify - -``` -spaces/chatstudio/newos4/ -├── index.html (lines 684, 770) -├── help.html (line 1149) -├── packages.json (lines 7-167) -└── modules/core/ - ├── module-loader.js (lines 35-37, 102) - ├── module-aiassist.js (lines 537-547, 730) - ├── ollama-manager.js (lines 8, 42-45, 70, 140) - └── terminal.js (lines 629, 691) -``` - ---- - -## Estimated Effort - -| Category | Time | Complexity | -|----------|------|------------| -| File fixes | 15 min | Low | -| Agent fixes | 30 min | Medium | -| AI routing | 10 min | Low | -| Testing | 20 min | Medium | -| **Total** | **~75 min** | | - ---- - -## Approval Required - -**Ready to execute?** Reply with: -- ✅ "Execute all" — apply all 13 fixes -- ✅ "Execute high priority" — apply top 4 fixes only -- ✅ "Skip #X" — exclude specific fixes -- ❓ "Discuss #X" — more details on specific issue diff --git a/chat/chat-modules.js b/chat/chat-modules.js index 8483d48..26ad115 100644 --- a/chat/chat-modules.js +++ b/chat/chat-modules.js @@ -296,7 +296,15 @@ async function generateModuleWithAI() { if (!response.ok) throw new Error('AI request failed'); var data = await response.json(); - var result = JSON.parse(data.response); + var result; + try { + result = JSON.parse(data.response); + } catch (e) { + showToast('AI response was not valid JSON. Try a different model.'); + btn.disabled = false; + btn.textContent = 'Generate with AI'; + return; + } document.getElementById('moduleName').value = result.name || ''; document.getElementById('moduleDesc').value = result.desc || ''; @@ -361,5 +369,7 @@ function importModules() { // Load modules from storage (runs after core.js) -// Auto-init once DOM + deps are ready -document.addEventListener('DOMContentLoaded', function() { loadModules(); }); +// Auto-init — DOM is already ready when this script loads dynamically +if (document.getElementById('chat-overlay')) { + loadModules(); +} diff --git a/chat/chat-ui.html b/chat/chat-ui.html index 5eedce6..9f6dd4b 100644 --- a/chat/chat-ui.html +++ b/chat/chat-ui.html @@ -95,6 +95,11 @@ <label>Commands (comma-separated)</label> <input type="text" id="moduleCommands" placeholder="e.g., /search, /find, /web"> </div> + <div class="form-group" style="margin-top:16px;padding-top:16px;border-top:1px solid var(--border);"> + <label>Generate with AI</label> + <textarea id="aiModuleDesc" placeholder="Describe the module you want... e.g. A code review assistant that checks for security issues" style="min-height:60px;"></textarea> + <button class="btn btn-secondary" style="width:100%;margin-top:8px;" onclick="generateModuleWithAI(event)">🤖 Generate with AI</button> + </div> </div> <div class="modal-footer"> <button class="btn btn-secondary" onclick="closeModuleModal()">Cancel</button> diff --git a/cheatsheet.html b/cheatsheet.html index 4cb4a1f..6245807 100644 --- a/cheatsheet.html +++ b/cheatsheet.html @@ -34,7 +34,7 @@ pre{background:var(--bg2);border:1px solid var(--border);border-radius:var(--rad <body> <h1>⚡ newOS4 Quick Reference</h1> -<div class="sub">WebX11 v4.2 · <a href="help.html">Full docs →</a> · <span class="no-print">Ctrl+P to print</span></div> +<div class="sub">WebX11 v4.3 · <a href="help.html">Full docs →</a> · <span class="no-print">Ctrl+P to print</span></div> <div class="cols"> @@ -104,13 +104,25 @@ pre{background:var(--bg2);border:1px solid var(--border);border-radius:var(--rad <div class="item"><span class="cmd">cron-list / cron-stop</span><span class="desc">Manage jobs</span></div> <div class="item"><span class="cmd">wpm</span><span class="desc">Package manager</span></div> <div class="item"><span class="cmd">mindmap</span><span class="desc">Brainstorming tool</span></div> -<div class="item"><span class="cmd">webdav-status</span><span class="desc">Cloud sync status</span></div> -<div class="item"><span class="cmd">webdav-push / pull</span><span class="desc">Sync files</span></div> -<div class="item"><span class="cmd">theme <name></span><span class="desc">Change theme</span></div> -<div class="item"><span class="cmd">voice</span><span class="desc">Speech-to-text</span></div> -<div class="item"><span class="cmd">chat</span><span class="desc">Chat overlay</span></div> -<div class="item"><span class="cmd">gzip / gunzip</span><span class="desc">Compress files</span></div> -<div class="item"><span class="cmd">encrypt / decrypt</span><span class="desc">AES-256 encrypt</span></div> + <div class="item"><span class="cmd">webdav-status</span><span class="desc">Cloud sync status</span></div> + <div class="item"><span class="cmd">webdav-push / pull</span><span class="desc">Sync files</span></div> + <div class="item"><span class="cmd">webdav-browser</span><span class="desc">File-tree UI browser</span></div> + <div class="item"><span class="cmd">webdav-rm / mkdir</span><span class="desc">Delete / create dir</span></div> + <div class="item"><span class="cmd">webdav-rename / move</span><span class="desc">Rename / move files</span></div> + <div class="item"><span class="cmd">webdav-backup</span><span class="desc">Backup space</span></div> + <div class="item"><span class="cmd">theme <name></span><span class="desc">Change theme</span></div> + <div class="item"><span class="cmd">voice</span><span class="desc">Speech-to-text</span></div> + <div class="item"><span class="cmd">chat</span><span class="desc">Chat overlay</span></div> + <div class="item"><span class="cmd">gzip / gunzip</span><span class="desc">Compress files</span></div> + <div class="item"><span class="cmd">encrypt / decrypt</span><span class="desc">AES-256 encrypt</span></div> + <div class="item"><span class="cmd">debug</span><span class="desc">Toggle debug panel</span></div> + <div class="item"><span class="cmd">agent-list</span><span class="desc">List registered agents</span></div> + <div class="item"><span class="cmd">lease-list</span><span class="desc">Show active leases</span></div> + <div class="item"><span class="cmd">orchestrate</span><span class="desc">Multi-step task agent</span></div> + <div class="item"><span class="cmd">security-level <0-3></span><span class="desc">Set security level</span></div> + <div class="item"><span class="cmd">cmd-allow / cmd-deny</span><span class="desc">Per-command access</span></div> + <div class="item"><span class="cmd">checkpoint</span><span class="desc">Snapshot VFS state</span></div> + <div class="item"><span class="cmd">health-check</span><span class="desc">System health check</span></div> </div> <h2>🐚 Shell</h2> @@ -137,13 +149,14 @@ pre{background:var(--bg2);border:1px solid var(--border);border-radius:var(--rad <div class="item"><span class="cmd">config</span><span class="desc">Config manager UI</span></div> </div> -<h2>🔒 Security</h2> +<h2>🔒 Security / Vault</h2> <div class="grid"> -<div class="item"><span class="cmd">security</span><span class="desc">Show current level</span></div> -<div class="item"><span class="cmd">security-level 0-3</span><span class="desc">Set level</span></div> -</div> -<div style="margin-top:8px"> -<p style="font-size:.78em;color:var(--text2)">Level 0 = permissive · 1 = standard (default) · 2 = paranoid · 3 = audit</p> +<div class="item"><span class="cmd">secret-set</span><span class="desc">Encrypt a secret</span></div> +<div class="item"><span class="cmd">secret-get</span><span class="desc">Decrypt a secret</span></div> +<div class="item"><span class="cmd">secret-list</span><span class="desc">List secrets</span></div> +<div class="item"><span class="cmd">secret-del</span><span class="desc">Delete a secret</span></div> +<div class="item"><span class="cmd">secret-lock</span><span class="desc">Lock vault</span></div> +<div class="item"><span class="cmd">secret-unlock</span><span class="desc">Unlock vault</span></div> </div> <h2>⌨️ Shortcuts</h2> @@ -159,7 +172,7 @@ pre{background:var(--bg2);border:1px solid var(--border);border-radius:var(--rad </div> <div class="footer"> - WebX11 v4.2 · <a href="help.html">help.html</a> · newOS4 on <a href="main.html">Launch →</a> + WebX11 v4.3 · <a href="help.html">help.html</a> · newOS4 on <a href="main.html">Launch →</a> </div> </body> @@ -4,7 +4,7 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <meta name="theme-color" content="#0a0a14"> -<title>WebX11 v4.2 — Help</title> +<title>WebX11 v4.3 — Help</title> <style> /* ── Reset & Base ── */ *,*::before,*::after{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent} @@ -231,9 +231,9 @@ footer .tagline{color:var(--accent2);margin-top:10px;font-weight:500} <h1>WebX11 <span>Docs</span></h1> </div> <div class="header-meta"> - <span class="badge badge-v">v4.2</span> - <span class="badge badge-new">26 core · 15 ext</span> - <span style="color:var(--text2)">135+ commands</span> + <span class="badge badge-v">v4.3</span> + <span class="badge badge-new">18 core · 14 ext</span> + <span style="color:var(--text2)">145+ commands</span> </div> </div> </header> @@ -250,6 +250,7 @@ footer .tagline{color:var(--accent2);margin-top:10px;font-weight:500} <a href="#web" class="nav-link">Search & Web</a> <a href="#voice" class="nav-link">Voice & Sound</a> <a href="#config" class="nav-link">Config</a> + <a href="#vault" class="nav-link">Vault</a> <a href="#modules" class="nav-link">Modules</a> <a href="#extras" class="nav-link">Extras</a> <a href="#busybox" class="nav-link">BusyBox</a> @@ -414,9 +415,9 @@ webx11.inputField // DOM input element</code></pre> <!-- ── Overview ── --> <section id="overview"> <h2>⛅ Overview</h2> - <p>WebX11 v4.2 is a browser-based Unix-like terminal — dynamic modules, AI assistant (Ollama), persistent virtual filesystem, web search, voice input, code editor, and a full skill system. Everything runs client-side in your browser.</p> + <p>WebX11 v4.3 is a browser-based Unix-like terminal — dynamic modules, AI assistant (Ollama), persistent virtual filesystem, web search, voice input, code editor, and a full skill system. Everything runs client-side in your browser.</p> <div class="feature-grid"> - <div class="feature-card"><h4>🧩 26 Core + 14 Ext</h4><p>Modules loaded at startup. Shell with pipes, redirects, vars, and scripts.</p></div> + <div class="feature-card"><h4>🧩 18 Core + 14 Ext</h4><p>Modules loaded at startup. Shell with pipes, redirects, vars, and scripts.</p></div> <div class="feature-card"><h4>🤖 Agent with Tools</h4><p>28 tools — file ops, web search, skills, modules. Destructive action confirmations with inline bar + countdown.</p></div> <div class="feature-card"><h4>📚 Skills System</h4><p>Persistent procedural memory. Create, patch, search, export skills for repeatable workflows.</p></div> <div class="feature-card"><h4>💾 VFS + Sessions</h4><p>Virtual filesystem persists in localStorage. Save/load/export sessions.</p></div> @@ -497,6 +498,17 @@ diff a.txt b.txt</pre> <tr><td><code>preview</code></td><td>Preview HTML in iframe</td><td><code>preview test.html</code></td></tr> <tr><td><code>factory-reset</code></td><td>Reset config</td><td><code>factory-reset</code></td></tr> <tr><td><code>reset-vfs</code></td><td>Reset VFS</td><td><code>reset-vfs</code></td></tr> + <tr><td><code>logrotate</code></td><td>Rotate current.log to dated archive</td><td><code>logrotate</code></td></tr> + <tr><td><code>health-check</code></td><td>Audit VFS, modules, logs, trash, skills</td><td><code>health-check</code></td></tr> + <tr><td><code>memory-map-export</code></td><td>Generate memory map from /memory/index.json</td><td><code>memory-map-export</code></td></tr> + <tr><td><code>commands</code></td><td>List commands with module source</td><td><code>commands</code></td></tr> + <tr><td><code>check</code></td><td>System health + module requirements</td><td><code>check</code></td></tr> + <tr><td><code>checkpoint</code></td><td>Create VFS snapshot</td><td><code>checkpoint</code></td></tr> + <tr><td><code>checkpoint-list</code></td><td>List VFS checkpoints</td><td><code>checkpoint-list</code></td></tr> + <tr><td><code>rollback</code></td><td>Restore VFS from checkpoint</td><td><code>rollback</code></td></tr> + <tr><td><code>checkpoint-del</code></td><td>Delete a checkpoint</td><td><code>checkpoint-del</code></td></tr> + <tr><td><code>smart-prompt</code></td><td>Toggle context-aware prompt</td><td><code>smart-prompt</code></td></tr> + <tr><td><code>test</code></td><td>Run test suite</td><td><code>test auto</code></td></tr> </table></div> <h3>AI Assistant</h3> @@ -509,6 +521,8 @@ diff a.txt b.txt</pre> <tr><td><code>ai-allow</code></td><td>Allow pending destructive action</td><td><code>ai-allow</code></td></tr> <tr><td><code>ai-deny</code></td><td>Deny pending destructive action</td><td><code>ai-deny</code></td></tr> <tr><td><code>ai-stop</code></td><td>Cancel running AI request</td><td><code>ai-stop</code></td></tr> + <tr><td><code>ai-compress</code></td><td>Manually compress conversation context</td><td><code>ai-compress</code></td></tr> + <tr><td><code>debug</code></td><td>Toggle AI debug mode</td><td><code>debug</code></td></tr> </table></div> <h3>Smart Context</h3> @@ -572,6 +586,10 @@ diff a.txt b.txt</pre> <tr><td><code>rm</code></td><td>Delete file</td><td><code>rm temp.txt</code></td></tr> <tr><td><code>mkdir</code></td><td>Create directory</td><td><code>mkdir projects</code></td></tr> <tr><td><code>rmdir</code></td><td>Remove directory</td><td><code>rmdir empty</code></td></tr> + <tr><td><code>trash</code></td><td>Move file to trash</td><td><code>trash temp.txt</code></td></tr> + <tr><td><code>trash-dir</code></td><td>Move directory to trash</td><td><code>trash-dir olddir</code></td></tr> + <tr><td><code>ls-trash</code></td><td>List items in trash</td><td><code>ls-trash</code></td></tr> + <tr><td><code>empty-trash</code></td><td>Permanently delete all trash items</td><td><code>empty-trash</code></td></tr> <tr><td><code>mv</code></td><td>Move/rename</td><td><code>mv old.txt new.txt</code></td></tr> <tr><td><code>cp</code></td><td>Copy file</td><td><code>cp a.txt b.txt</code></td></tr> <tr><td><code>touch</code></td><td>Create empty file</td><td><code>touch newfile.txt</code></td></tr> @@ -588,6 +606,12 @@ diff a.txt b.txt</pre> <tr><td><code>download</code></td><td>Download file</td><td><code>download file.txt</code></td></tr> <tr><td><code>vfs-export</code></td><td>Download VFS as JSON</td><td><code>vfs-export</code></td></tr> <tr><td><code>vfs-import</code></td><td>Import VFS from JSON</td><td><code>vfs-import</code></td></tr> + <tr><td><code>session-save</code></td><td>Save a session snapshot</td><td><code>session-save</code></td></tr> + <tr><td><code>session-load</code></td><td>Load a session snapshot</td><td><code>session-load mysession</code></td></tr> + <tr><td><code>session-list</code></td><td>List all saved sessions</td><td><code>session-list</code></td></tr> + <tr><td><code>session-delete</code></td><td>Delete a session</td><td><code>session-delete old</code></td></tr> + <tr><td><code>session-export</code></td><td>Export session as JSON download</td><td><code>session-export mysession</code></td></tr> + <tr><td><code>session-import</code></td><td>Import a session from file</td><td><code>session-import</code></td></tr> </table></div> </section> @@ -782,11 +806,31 @@ ollamaconnect</pre> </table></div> </section> + <!-- ── Secret Vault ── --> + <section id="vault"> + <h2>🔒 Secret Vault</h2> + <div class="callout callout-success"> + <span class="callout-title">Encrypted Secrets Manager</span> + AES-256-GCM encrypted key-value storage in localStorage. Lock/unlock with a PIN — secrets are never stored in plain text. Vault status persists across sessions. + </div> + <div class="table-wrap"><table> + <tr><th>Command</th><th>Description</th></tr> + <tr><td><code>secret-set</code></td><td>Encrypt and store a secret</td></tr> + <tr><td><code>secret-get</code></td><td>Decrypt and display a secret value</td></tr> + <tr><td><code>secret-list</code></td><td>List all stored secret names</td></tr> + <tr><td><code>secret-del</code></td><td>Remove a stored secret</td></tr> + <tr><td><code>secret-lock</code></td><td>Lock the vault (clears key from memory)</td></tr> + <tr><td><code>secret-unlock</code></td><td>Unlock the vault with PIN</td></tr> + <tr><td><code>secret-setpin</code></td><td>Set or change vault PIN</td></tr> + <tr><td><code>secret-status</code></td><td>Show vault status (locked/unlocked/pin set)</td></tr> + </table></div> + </section> + <!-- ── Modules ── --> <section id="modules"> <h2>🧩 Module System</h2> <div class="callout callout-info"> - <span class="callout-title">26 Core + 14 Ext</span> + <span class="callout-title">25 Core + 17 Ext</span> Modules loaded in dependency order at startup. Toggle ext modules with ⚙ gear on splash. </div> <h3>Commands</h3> @@ -796,6 +840,9 @@ ollamaconnect</pre> <tr><td><code>loadmod <url></code></td><td>Load module from URL</td></tr> <tr><td><code>removemod <name></code></td><td>Remove a module</td></tr> <tr><td><code>listmods</code></td><td>List all loaded modules</td></tr> + <tr><td><code>load-local</code></td><td>Load module from VFS or URL</td></tr> + <tr><td><code>loadlocal</code></td><td>Alias for load-local</td></tr> + <tr><td><code>startup-modules</code></td><td>Configure boot modules</td></tr> </table></div> <h3>Boot Order</h3> @@ -826,8 +873,8 @@ ollamaconnect</pre> <tr><td><code>webdav-backup</code></td><td>Backup VFS</td></tr> <tr><td><code>webdav-restore</code></td><td>Restore VFS</td></tr> <tr><td><code>webdav-backup-list</code></td><td>List backups</td></tr> - <tr><td><code>webdav-browser</code></td><td>Visual file explorer modal</td></tr> <tr><td><code>webdav-explorer</code></td><td>Interactive text browser</td></tr> + <tr><td><code>webdav-browser</code></td><td>Visual file explorer modal</td></tr> </table></div> <h3>Calculator & Cron</h3> @@ -1004,6 +1051,67 @@ vfs-import</pre> <p>~5–10MB per domain. Only URLs (not code) are stored for modules.</p> </section> + <!-- ── WebDAV ── --> + <section id="webdav"> + <h2>☁️ WebDAV</h2> + <p>Cloud sync with your WebX11 space. File-tree browser UI with bulk actions.</p> + <div class="table-wrap"><table> + <tr><th>Command</th><th>Description</th></tr> + <tr><td><code>webdav-config <url> <token></code></td><td>Configure WebDAV connection</td></tr> + <tr><td><code>webdav-status</code></td><td>Show sync status and connection health</td></tr> + <tr><td><code>webdav-ls [path]</code></td><td>List remote files</td></tr> + <tr><td><code>webdav-push <local> [remote]</code></td><td>Upload file to cloud</td></tr> + <tr><td><code>webdav-pull <remote> [local]</code></td><td>Download file from cloud</td></tr> + <tr><td><code>webdav-browser</code></td><td>Open file-tree browser UI with checkboxes, context menu, inline rename</td></tr> + <tr><td><code>webdav-rm <path></code></td><td>Delete remote file/directory</td></tr> + <tr><td><code>webdav-mkdir <path></code></td><td>Create remote directory</td></tr> + <tr><td><code>webdav-rename <old> <new></code></td><td>Rename remote file</td></tr> + <tr><td><code>webdav-move <src> <dst></code></td><td>Move remote file (alias for rename)</td></tr> + <tr><td><code>webdav-backup</code></td><td>Backup entire space</td></tr> + </table></div> + <h3>Browser Features</h3> + <ul> + <li>Checkbox select + Select All toggle</li> + <li>Bulk action bar: Delete, Move, Download selected files</li> + <li>Inline rename: double-click filename</li> + <li>Right-click context menu with all operations</li> + <li>Breadcrumb navigation</li> + <li>Create-directory button</li> + <li>Keyboard shortcuts: <code>j</code>/<code>k</code> navigate, <code>Space</code> select, <code>Enter</code> open</li> + </ul> + </section> + + <!-- ── Agents ── --> + <section id="agents"> + <h2>🤖 Agent Orchestration</h2> + <p>Register, list, and delegate tasks to AI agents with lease-based lifecycle management.</p> + <div class="table-wrap"><table> + <tr><th>Command</th><th>Description</th></tr> + <tr><td><code>agent-list</code></td><td>List all registered agents with status</td></tr> + <tr><td><code>agent-register <name> <model></code></td><td>Register a new AI agent</td></tr> + <tr><td><code>lease-list</code></td><td>Show active leases — agent tasks with status and TTL</td></tr> + <tr><td><code>orchestrate <task></code></td><td>Delegate multi-step task to agent orchestrator</td></tr> + </table></div> + </section> + + <!-- ── Debug ── --> + <section id="debug-panel"> + <h2>🔧 Debug Panel</h2> + <p>Toggle the debug overlay to monitor system internals in real-time.</p> + <div class="table-wrap"><table> + <tr><th>Command</th><th>Description</th></tr> + <tr><td><code>debug</code></td><td>Toggle debug panel on/off</td></tr> + </table></div> + <h3>Debug Panel Shows</h3> + <ul> + <li><strong>Memory</strong> — localStorage usage, VFS size, session context size</li> + <li><strong>Events</strong> — registered event listeners, module event bindings</li> + <li><strong>Errors</strong> — recent JS errors with stack traces</li> + <li><strong>Modules</strong> — loaded count, broken references, cache state</li> + </ul> + <p>Useful for troubleshooting module load failures, VFS corruption, and AI context bloat.</p> + </section> + <!-- ── Security ── --> <section id="security"> <h2>🔒 Security</h2> @@ -1014,9 +1122,23 @@ vfs-import</pre> <tr><td>No eval() on User Input</td><td>✅</td></tr> <tr><td>Path Traversal Prevention</td><td>✅</td></tr> <tr><td>Sandboxed File System</td><td>✅</td></tr> - <tr><td>AI Destructive Confirmations</td><td>✅ Required + timeout</td></tr> + <tr><td>AI Destructive Confirmations</td><td>✅ Required + 120s countdown</td></tr> <tr><td>Iframe Preview Sandbox</td><td>✅ allow-same-origin allow-scripts</td></tr> </table></div> + <h3>Security Levels</h3> + <div class="table-wrap"><table> + <tr><th>Level</th><th>Behavior</th></tr> + <tr><td><code>0</code></td><td>Unrestricted — all commands allowed without confirmation</td></tr> + <tr><td><code>1</code> (default)</td><td>Confirm destructive actions (rm, overwrite, etc.)</td></tr> + <tr><td><code>2</code></td><td>Deny sensitive commands (rm -rf, encrypt operations)</td></tr> + <tr><td><code>3</code></td><td>Deny all dangerous commands (requires deliberate override)</td></tr> + </table></div> + <div class="table-wrap"><table> + <tr><th>Command</th><th>Description</th></tr> + <tr><td><code>security-level <0-3></code></td><td>Set active security level</td></tr> + <tr><td><code>cmd-allow <command></code></td><td>Add command to allowlist (bypasses restrictions)</td></tr> + <tr><td><code>cmd-deny <command></code></td><td>Add command to denylist (always blocked)</td></tr> + </table></div> <div class="callout callout-warning"> <span class="callout-title">⚠️ Module Loading</span> Only load modules from trusted sources — they have full terminal access. @@ -1052,7 +1174,7 @@ listmods</pre> <section id="ref"> <h2>⚡ Quick Reference</h2> <div class="quick-ref"> - <div class="quick-ref-title">WebX11 v4.2</div> + <div class="quick-ref-title">WebX11 v4.3</div> <div class="quick-ref-grid"> <div class="quick-ref-item"><span class="cmd">help</span><span class="desc">Show all commands</span></div> <div class="quick-ref-item"><span class="cmd">cls</span><span class="desc">Clear terminal</span></div> @@ -1096,6 +1218,21 @@ listmods</pre> <h2>📋 Changelog</h2> <div class="version-item"> + <div class="v-header"><span class="v-num">v4.3 — June 2026</span><span class="v-date">2026-06-02</span></div> + <ul> + <li><strong>WebDAV Browser</strong> — Modern file-tree UI with checkboxes, Select All, bulk actions (Delete, Move, Download), inline rename (double-click), context menu, breadcrumbs, keyboard shortcuts</li> + <li><strong>WebDAV commands</strong> — <code>webdav-browser</code>, <code>webdav-rm</code>, <code>webdav-mkdir</code>, <code>webdav-rename</code>, <code>webdav-move</code>, <code>webdav-backup</code></li> + <li><strong>Skill Pack System</strong> — Auto-loads 12 skills on startup, seed/reload support, skill-pack-core.js manages in-memory index</li> + <li><strong>Agent Orchestration</strong> — <code>agent-list</code>, <code>agent-register</code>, <code>lease-list</code>, <code>orchestrate</code> for multi-step delegation</li> + <li><strong>Security Layer</strong> — <code>security-level 0-3</code> with granular restrictions, <code>cmd-allow</code>/<code>cmd-deny</code> per-command access control</li> + <li><strong>Debug Panel</strong> — <code>debug</code> toggle shows memory, events, errors, module health</li> + <li><strong>Chat Cleanup</strong> — Removed <code>chat-setapi</code>, <code>chat-setmodel</code>, <code>chat-ask</code>; single <code>chat</code> command toggles overlay</li> + <li>Module count refactored: 18 core + 14 ext (was 25+17)</li> + <li>Cache: <code>skillpack1</code>/<code>chatfix1</code>/<code>webdav2</code> — hard refresh required</li> + </ul> + </div> + + <div class="version-item"> <div class="v-header"><span class="v-num">v4.2.1 — May 2026</span><span class="v-date">2026-05-27</span></div> <ul> <li><strong>BusyBox</strong> — 16 new utility commands: <code>which</code>, <code>env</code>, <code>date</code>, <code>cal</code>, <code>yes</code>, <code>seq</code>, <code>base64</code>, <code>printf</code>, <code>cut</code>, <code>uniq</code>, <code>hostname</code>, <code>whoami</code>, <code>basename</code>, <code>dirname</code>, <code>sleep</code>, <code>expand</code></li> @@ -1165,7 +1302,7 @@ listmods</pre> <a href="./main.html">Launch WebX11</a> <a href="https://ollama.com" target="_blank">Ollama</a> </div> - <p>WebX11 v4.2 · 2026</p> + <p>WebX11 v4.3 · 2026</p> <p class="tagline">Happy Hacking 🖥️🤖🎤</p> </footer> @@ -1250,7 +1387,7 @@ document.querySelectorAll('.doc-tabs input').forEach(radio=>{ } })(); -console.log('WebX11 v4.2 Docs — Modern'); +console.log('WebX11 v4.3 Docs — Modern'); </script> </body> </html> diff --git a/modules/core/module-agent-registry.js b/modules/core/module-agent-registry.js new file mode 100644 index 0000000..367f8b1 --- /dev/null +++ b/modules/core/module-agent-registry.js @@ -0,0 +1,79 @@ +// module-agent-registry.js — VFS-backed agent directory +// Command: agent-list, agent-register, agent-unregister, agent-status +class AgentRegistryModule { + constructor() { + this.commands = {}; + this.terminal = null; + } + registerCommands() { + this.commands['agent-list'] = () => this.listAgents(); + this.commands['agent-status'] = (args) => this.showAgentStatus(args[0]); + this.commands['agent-register'] = (args) => this.registerAgent(args); + this.commands['agent-unregister'] = (args) => this.unregisterAgent(args[0]); + } + init(terminal) { + this.terminal = terminal; + if (!this.terminal.fs.readFile('/agents/')) { + this.terminal.fs.mkdir('agents'); + } + this.terminal.print('📋 Agent Registry loaded', 'info'); + } + listAgents() { + const agents = this.terminal.fs.ls('/agents/'); + if (!agents || agents.length === 0) { + return this.terminal.print('No agents registered', 'info'); + } + this.terminal.print(`📋 Registered Agents (${agents.length}):`, 'header'); + agents.forEach(a => { + if (a.type === 'dir' && a.name !== 'configured') { + const data = this.terminal.fs.readFile(`/agents/${a.name}/status.json`); + const s = data ? JSON.parse(data) : { status: 'unknown', capabilities: [] }; + this.terminal.print(` ${a.name} — ${s.status} (${(s.capabilities || []).length} capabilities)`, 'info'); + } + }); + } + registerAgent(args) { + const id = args[1] || `agent-${Date.now()}`; + const capabilities = args.slice(2).length ? args.slice(2) : ['list_files', 'read_file']; + const agentDir = `agents/${id}`; + if (!this.terminal.fs.readFile('/' + agentDir + '/')) this.terminal.fs.mkdir(agentDir); + this.terminal.fs.writeFile(`${agentDir}/status.json`, JSON.stringify({ + id, + status: 'idle', + capabilities, + registered: new Date().toISOString(), + lastHeartbeat: new Date().toISOString() + })); + this.terminal.print(`✅ Agent '${id}' registered with ${capabilities.length} capabilities`, 'success'); + } + unregisterAgent(id) { + const ok = this.terminal.fs.deleteFile(`agents/${id}`); + if (ok) { + this.terminal.print(`✅ Agent '${id}' unregistered`, 'success'); + } else { + this.terminal.print(`❌ Agent '${id}' not found`, 'error'); + } + } + showAgentStatus(id) { + if (!id) return this.terminal.print('Usage: agent-status <agent-id>', 'error'); + const data = this.terminal.fs.readFile(`/agents/${id}/status.json`); + if (!data) return this.terminal.print(`❌ Agent '${id}' not found`, 'error'); + const s = JSON.parse(data); + this.terminal.print(`Agent: ${s.id}`, 'header'); + this.terminal.print(` Status: ${s.status}`, 'info'); + this.terminal.print(` Capabilities: ${(s.capabilities || []).join(', ')}`, 'info'); + this.terminal.print(` Registered: ${s.registered}`, 'info'); + this.terminal.print(` Last Heartbeat: ${s.lastHeartbeat}`, 'info'); + } + getAgent(id) { + const data = this.terminal.fs.readFile(`/agents/${id}/status.json`); + return data ? JSON.parse(data) : null; + } + updateHeartbeat(id) { + const agent = this.getAgent(id); + if (!agent) return false; + agent.lastHeartbeat = new Date().toISOString(); + this.terminal.fs.writeFile(`agents/${id}/status.json`, JSON.stringify(agent)); + return true; + } +}
\ No newline at end of file diff --git a/modules/core/module-aiassist.js b/modules/core/module-aiassist.js index edc5225..eb41af9 100644 --- a/modules/core/module-aiassist.js +++ b/modules/core/module-aiassist.js @@ -384,6 +384,40 @@ RULES: description: 'Session aggregate stats', parameters: { type: 'object', properties: {}, required: [] } } + }, { + type: 'function', + function: { + name: 'list_agents', + description: 'List registered agent IDs, status, and capabilities', + parameters: { type: 'object', properties: {}, required: [] } + } + }, { + type: 'function', + function: { + name: 'delegate_task', + description: '(DESTRUCTIVE) Delegate a task to a worker agent. Auto-spawns if needed.', + parameters: { + type: 'object', + properties: { + task: { type: 'string', description: 'The task description to delegate' } + }, + required: ['task'] + } + } + }, { + type: 'function', + function: { + name: 'spawn_agent', + description: '(DESTRUCTIVE) Spawn a new worker agent with a role and capabilities', + parameters: { + type: 'object', + properties: { + role: { type: 'string', description: 'Agent role (e.g. researcher, writer)' }, + capabilities: { type: 'string', description: 'Comma-separated capabilities (optional)' } + }, + required: ['role'] + } + } }]; } getHelpInfo() { @@ -568,7 +602,14 @@ RULES: (entry.error ? ' <span class="debug-err">⚠️</span>' : '') + '</div>'; } - panel.innerHTML = '<div class="debug-header" style="display:flex;justify-content:space-between;align-items:center;padding:6px 10px;border-bottom:1px solid #333;font-weight:bold;color:var(--success-color);font-size:0.9em;">🔧 Tool Log <span class="debug-count" style="color:var(--text-muted);font-weight:normal;font-size:0.85em;">' + this._toolCallLog.length + ' calls</span><span style="cursor:pointer;color:var(--text-muted);font-size:1.1em;touch-action:manipulation;" onclick="var p=document.getElementById(\'debug-panel\');if(p)p.style.display=\'none\';var a=window.webx11&&webx11.moduleManager&&webx11.moduleManager.modules&&webx11.moduleManager.modules.WebX11AIAssistantModule;if(a)a._debugPanelEnabled=false;">✕</span></div>' + html; + panel.innerHTML = '<div class="debug-header" style="display:flex;justify-content:space-between;align-items:center;padding:6px 10px;border-bottom:1px solid #333;font-weight:bold;color:var(--success-color);font-size:0.9em;flex-wrap:wrap;gap:4px;">' + + '<span>🔧 Tool Log <span class="debug-count" style="color:var(--text-muted);font-weight:normal;font-size:0.85em;">' + this._toolCallLog.length + ' calls</span></span>' + + '<span style="display:flex;gap:4px;">' + + '<button onclick="var m=webx11.moduleManager.modules.WebX11AIAssistantModule;if(m){var t=m._toolCallLog.map(function(e){return\'#\'+e.turn+\' \'+e.name+\' (\'+e.duration+\'s)\'+(e.error?\' ⚠️\':\'\')+\'\\n\'+JSON.stringify(e.args).substring(0,80)}).join(\'\\n\\n\');navigator.clipboard.writeText(t).then(function(){m.terminal&&m.terminal.print(\'📋 Debug log copied to clipboard\',\'success\')}).catch(function(){prompt(\'Copy debug log:\',t)})}" style="background:var(--bg-tertiary);border:1px solid var(--border-color);color:var(--text-color);padding:2px 6px;border-radius:3px;cursor:pointer;font-size:0.75em;min-height:24px;">📋</button>' + + '<button onclick="var m=webx11.moduleManager.modules.WebX11AIAssistantModule;if(m){var json=JSON.stringify(m._toolCallLog,null,2);var b=new Blob([json],{type:\'application/json\'});var u=URL.createObjectURL(b);var a=document.createElement(\'a\');a.href=u;a.download=\'debug-log-\'+new Date().toISOString().slice(0,19)+\'.json\';document.body.appendChild(a);a.click();setTimeout(function(){document.body.removeChild(a);URL.revokeObjectURL(u)},2000);m.terminal&&m.terminal.print(\'📥 Debug log exported\',\'success\')}" style="background:var(--bg-tertiary);border:1px solid var(--border-color);color:var(--text-color);padding:2px 6px;border-radius:3px;cursor:pointer;font-size:0.75em;min-height:24px;">📥</button>' + + '<button onclick="var m=webx11.moduleManager.modules.WebX11AIAssistantModule;if(m){var existing=[];var raw=m.terminal.fs.readFile(\'/debug.log\');if(raw){try{existing=JSON.parse(raw);}catch(e){}}var log=m._toolCallLog;var seen={};existing.forEach(function(e){seen[e.turn]=true;});log.forEach(function(e){if(!seen[e.turn]){existing.push(e);seen[e.turn]=true;}});var json=JSON.stringify(existing,null,2);m.terminal.fs.writeFile(\'/debug.log\',json);m.terminal.print(\'💾 Debug log appended — \'+existing.length+\' entries total\',\'success\')}" style="background:var(--bg-tertiary);border:1px solid var(--border-color);color:var(--text-color);padding:2px 6px;border-radius:3px;cursor:pointer;font-size:0.75em;min-height:24px;">💾</button>' + + '<span style="cursor:pointer;color:var(--text-muted);font-size:1.1em;touch-action:manipulation;" onclick="var p=document.getElementById(\'debug-panel\');if(p)p.style.display=\'none\';var a=window.webx11&&webx11.moduleManager&&webx11.moduleManager.modules&&webx11.moduleManager.modules.WebX11AIAssistantModule;if(a)a._debugPanelEnabled=false;">✕</span>' + + '</span></div>' + html; } _isToolError(result) { if (!result || typeof result !== 'string') return null; @@ -1194,6 +1235,37 @@ RULES: for (const [, meta] of entries) totalTasks += meta.tasks || 0; return JSON.stringify({ totalSessions: entries.length, totalTasks, dates: entries.map(([d]) => d) }); } + case 'list_agents': { + const registry = this.terminal.moduleManager.modules['AgentRegistryModule']; + if (!registry) return JSON.stringify({ error: 'Agent registry not available' }); + const agents = this.terminal.fs.ls('/agents/'); + const results = []; + if (agents) { + agents.forEach(a => { + if (a.type === 'dir' && a.name !== 'configured') { + const data = this.terminal.fs.readFile(`/agents/${a.name}/status.json`); + if (data) results.push(JSON.parse(data)); + } + }); + } + return JSON.stringify({ agents: results, count: results.length }); + } + case 'delegate_task': { + const task = a.task || ''; + if (!task) return JSON.stringify({ error: 'No task provided' }); + const orch = this.terminal.moduleManager.modules['OrchestratorModule']; + if (!orch) return JSON.stringify({ error: 'Orchestrator not available' }); + orch.delegateTask(task); + return JSON.stringify({ message: `Task delegated: ${task.substring(0, 80)}` }); + } + case 'spawn_agent': { + const role = a.role || 'worker'; + const caps = a.capabilities ? a.capabilities.split(',').map(s => s.trim()) : ['list_files', 'read_file']; + const orch = this.terminal.moduleManager.modules['OrchestratorModule']; + if (!orch) return JSON.stringify({ error: 'Orchestrator not available' }); + const agent = orch.spawnAgent(role, caps); + return JSON.stringify({ message: `Agent spawned: ${agent.id}`, agent }); + } case 'load_skills': { const query = (a && a.query || '').toLowerCase(); const fs = this.terminal.fs; diff --git a/modules/core/module-chatstudio.js b/modules/core/module-chatstudio.js index 762eae3..9643fff 100644 --- a/modules/core/module-chatstudio.js +++ b/modules/core/module-chatstudio.js @@ -17,32 +17,12 @@ class ChatStudioModule { getHelpInfo() { return { - 'chat': 'Toggle the ChatStudio overlay', - 'chat-setapi': 'Set the API base URL for ChatStudio', - 'chat-setmodel': 'Set the default model for ChatStudio', - 'chat-ask': 'Ask a question directly via ChatStudio' + 'chat': 'Toggle the ChatStudio overlay (inside: /setapi, /setmodel, /ask, /help)', }; } registerCommands() { this.commands['chat'] = () => this.toggleChatOverlay(); - this.commands['chat-setapi'] = (args) => { - if (!args[0]) { - const base = window.API_BASE || '/api'; - this.terminal.print('Current API base: ' + base, 'info'); - this.terminal.print('Usage: chat-setapi <base-url>', 'info'); - this.terminal.print('Example: chat-setapi /api', 'info'); - return; - } - const url = args[0].replace(/\/+$/, ''); - window.API_BASE = url; - try { - var settings = JSON.parse(localStorage.getItem('webx11_chat_settings') || '{}'); - settings.apiBase = url; - localStorage.setItem('webx11_chat_settings', JSON.stringify(settings)); - } catch (e) {} - this.terminal.print('API base set to: ' + url + ' (reopen chat to apply)', 'success'); - }; } toggleChatOverlay() { @@ -59,7 +39,7 @@ class ChatStudioModule { if (!this._cssLoaded) { const link = document.createElement('link'); link.rel = 'stylesheet'; - link.href = 'chat/chat-ui.css?v=4'; + link.href = 'chat/chat-ui.css?v=5'; link.onload = () => { this._cssLoaded = true; }; link.onerror = () => reject(new Error('Failed to load chat/chat-ui.css')); document.head.appendChild(link); @@ -120,7 +100,7 @@ class ChatStudioModule { resolve(); return; } - fetch('chat/chat-ui.html?v=4') + fetch('chat/chat-ui.html?v=5') .then(r => r.text()) .then(html => { this._overlayHtml = html; // cache for re-open @@ -134,7 +114,7 @@ class ChatStudioModule { const loadScript = (src) => { return new Promise((res, rej) => { const s = document.createElement('script'); - s.src = src; + s.src = src + '?v=5'; s.onload = () => res(); s.onerror = () => rej(new Error('Failed to load ' + src)); document.body.appendChild(s); @@ -142,8 +122,8 @@ class ChatStudioModule { }; Promise.all([ - loadScript('chat/chat-commands.js?v=4'), - loadScript('chat/chat-modules.js?v=4') + loadScript('chat/chat-commands.js'), + loadScript('chat/chat-modules.js') ]).then(() => { resolve(); }); diff --git a/modules/core/module-fs.js b/modules/core/module-fs.js index da2e740..b119343 100644 --- a/modules/core/module-fs.js +++ b/modules/core/module-fs.js @@ -279,18 +279,44 @@ class FileSystemModule { this.commands['upload'] = () => { document.getElementById('file-upload-input').click(); }; this.commands['download'] = (args) => { if(!args[0]) return this.terminal.print('Usage: download <filename>', 'warning'); - const content = this.terminal.fs.readFile(args[0]); + const fname = args[0]; + const content = this.terminal.fs.readFile(fname); if(content !== null && content !== undefined) { + const size = content.length; + const sizeStr = size > 1024 ? (size / 1024).toFixed(1) + ' KB' : size + ' B'; + // Desktop: programmatic click (works on desktop Chrome/Firefox) const blob = new Blob([content], {type: 'application/octet-stream'}); const url = URL.createObjectURL(blob); const a = document.createElement('a'); - a.href = url; a.download = args[0]; + a.href = url; a.download = fname; a.style.display = 'none'; document.body.appendChild(a); a.click(); setTimeout(() => { document.body.removeChild(a); URL.revokeObjectURL(url); }, 2000); - this.terminal.print('Downloaded: ' + args[0], 'success'); - } else { this.terminal.print('download: ' + args[0] + ': No such file', 'error'); } + // Mobile fallback: build a DOM button with real user gesture + const line = document.createElement('div'); + line.className = 'output-line'; + const msg = document.createElement('span'); + msg.textContent = '⬇ ' + fname + ' (' + sizeStr + ') '; + const btn = document.createElement('button'); + btn.textContent = '⬇ Download'; + btn.className = 'btn'; + btn.style.cssText = 'background:var(--bg-tertiary);border:1px solid var(--border-color);color:var(--success-color);padding:4px 12px;border-radius:4px;cursor:pointer;font-size:0.85em;min-height:36px;'; + btn.onclick = () => { + const b = new Blob([this.terminal.fs.readFile(fname)], {type: 'application/octet-stream'}); + const u = URL.createObjectURL(b); + const el = document.createElement('a'); + el.href = u; el.download = fname; + el.style.display = 'none'; + document.body.appendChild(el); + el.click(); + setTimeout(() => { document.body.removeChild(el); URL.revokeObjectURL(u); }, 2000); + }; + line.appendChild(msg); + line.appendChild(btn); + this.terminal.outputContainer.appendChild(line); + this.terminal.scrollToBottom(); + } else { this.terminal.print('download: ' + fname + ': No such file', 'error'); } }; this.commands['vfs-export'] = () => { const data = this.terminal.fs.root; diff --git a/modules/core/module-leases.js b/modules/core/module-leases.js new file mode 100644 index 0000000..b79870c --- /dev/null +++ b/modules/core/module-leases.js @@ -0,0 +1,102 @@ +// module-leases.js — Lease system for agent task delegation +// Commands: lease-create, lease-list, lease-revoke, lease-renew +class LeaseModule { + constructor() { + this.commands = {}; + this.terminal = null; + this._leases = {}; + this._watchdogTimer = null; + } + registerCommands() { + this.commands['lease-create'] = (args) => this.createLease(args); + this.commands['lease-list'] = () => this.listLeases(); + this.commands['lease-revoke'] = (args) => this.revokeLease(args[0]); + this.commands['lease-renew'] = (args) => this.renewLease(args[0], parseInt(args[1]) || 30); + } + init(terminal) { + this.terminal = terminal; + if (!this.terminal.fs.readFile('/leases/')) { + this.terminal.fs.mkdir('leases'); + } + // Restore persisted leases + const files = this.terminal.fs.ls('/leases/'); + if (files) { + files.forEach(f => { + if (f.name.endsWith('.json')) { + const data = this.terminal.fs.readFile(`/leases/${f.name}`); + if (data) { + try { + const lease = JSON.parse(data); + this._leases[lease.id] = lease; + } catch (e) {} + } + } + }); + } + // Start watchdog every 5 seconds + this._watchdogTimer = setInterval(() => this._checkExpired(), 5000); + this.terminal.print('📜 Lease system loaded', 'info'); + } + createLease(args) { + const workerId = args[1]; + const duration = parseInt(args[2]) || 30; + const task = args.slice(3).join(' ') || 'generic task'; + if (!workerId) return this.terminal.print('Usage: lease-create <agent-id> [duration_sec] [task]', 'error'); + + const lease = { + id: `lease-${Date.now()}`, + workerId, + orchestratorId: 'orchestrator-0', + task, + createdAt: new Date().toISOString(), + expiresAt: new Date(Date.now() + duration * 1000).toISOString(), + status: 'active', + duration + }; + this._leases[lease.id] = lease; + this.terminal.fs.writeFile(`leases/${lease.id}.json`, JSON.stringify(lease)); + this.terminal.print(`📜 Lease ${lease.id} created for ${workerId} (${duration}s, task: ${task.substring(0, 40)})`, 'success'); + return lease; + } + listLeases() { + const all = Object.values(this._leases); + const active = all.filter(l => l.status === 'active'); + const expired = all.filter(l => l.status === 'expired'); + const revoked = all.filter(l => l.status === 'revoked'); + this.terminal.print(`📜 Leases: ${active.length} active, ${expired.length} expired, ${revoked.length} revoked`, 'header'); + active.forEach(l => { + const remaining = Math.max(0, Math.round((new Date(l.expiresAt) - Date.now()) / 1000)); + this.terminal.print(` ${l.id} → ${l.workerId} (${remaining}s remain) — ${l.task.substring(0, 40)}`, 'info'); + }); + } + revokeLease(id) { + if (!this._leases[id]) return this.terminal.print(`❌ Lease ${id} not found`, 'error'); + this._leases[id].status = 'revoked'; + this._leases[id].revokedAt = new Date().toISOString(); + this.terminal.fs.writeFile(`leases/${id}.json`, JSON.stringify(this._leases[id])); + this.terminal.print(`✅ Lease ${id} revoked`, 'warning'); + } + renewLease(id, additionalSeconds) { + if (!this._leases[id] || this._leases[id].status !== 'active') { + return this.terminal.print(`❌ Lease ${id} not found or not active`, 'error'); + } + const lease = this._leases[id]; + const newExpiry = new Date(Date.now() + additionalSeconds * 1000); + lease.expiresAt = newExpiry.toISOString(); + lease.duration += additionalSeconds; + lease.renewedAt = new Date().toISOString(); + this.terminal.fs.writeFile(`leases/${id}.json`, JSON.stringify(lease)); + this.terminal.print(`✅ Lease ${id} renewed (+${additionalSeconds}s)`, 'success'); + } + _checkExpired() { + const now = Date.now(); + Object.values(this._leases).forEach(lease => { + if (lease.status === 'active' && new Date(lease.expiresAt) < now) { + lease.status = 'expired'; + lease.expiredAt = new Date().toISOString(); + this.terminal.fs.writeFile(`leases/${lease.id}.json`, JSON.stringify(lease)); + this.terminal.log('INFO', 'Lease', `Lease ${lease.id} for ${lease.workerId} expired`); + } + }); + } +}
\ No newline at end of file diff --git a/modules/core/module-loader.js b/modules/core/module-loader.js index 7187fec..2ffdb17 100644 --- a/modules/core/module-loader.js +++ b/modules/core/module-loader.js @@ -30,7 +30,10 @@ 'module-fetcher.js', 'module-skills.js', 'secret-vault.js', - 'module-chatstudio.js' + 'module-chatstudio.js', + 'module-agent-registry.js', + 'module-leases.js', + 'module-orchestrator.js' ]; function getSelectedModules() { @@ -99,7 +102,7 @@ window.__moduleLoaderReload = reloadWithSelection; -const LATEST_VER = '2026-06-02-fontfix3'; +const LATEST_VER = '2026-06-02-webdav2'; const CACHE = '?v=' + LATEST_VER; const stored = localStorage.getItem('webx11_cache_ver'); if (stored !== LATEST_VER) { diff --git a/modules/core/module-orchestrator.js b/modules/core/module-orchestrator.js new file mode 100644 index 0000000..4fab599 --- /dev/null +++ b/modules/core/module-orchestrator.js @@ -0,0 +1,151 @@ +// module-orchestrator.js — Multi-agent orchestration system +// Commands: orchestrate, orchestrator-status, orchestrator-spawn, orchestrator-kill, orchestrator-purge +class OrchestratorModule { + constructor() { + this.commands = {}; + this.terminal = null; + this._agents = {}; + this._SPAWN_LIMIT = 3; + this._heartbeatInterval = null; + } + registerCommands() { + this.commands['orchestrate'] = (args) => this.delegateTask(args.join(' ')); + this.commands['orchestrator-status'] = () => this.showStatus(); + this.commands['orchestrator-spawn'] = (args) => this.spawnAgent(args[0], args.slice(1)); + this.commands['orchestrator-kill'] = (args) => this.killAgent(args[0]); + this.commands['orchestrator-purge'] = () => this.purgeDeadAgents(); + } + init(terminal) { + this.terminal = terminal; + // Register orchestrator in the agent registry + const registry = this.terminal.moduleManager.modules['AgentRegistryModule']; + if (registry) { + registry.registerAgent(['register', 'orchestrator-0', 'delegate', 'spawn', 'monitor', 'purge']); + } + // Heartbeat watchdog every 3 seconds + this._heartbeatInterval = setInterval(() => this._checkHeartbeats(), 3000); + this.terminal.print('🧠 Orchestrator loaded — ready to delegate', 'success'); + } + spawnAgent(role, capabilities) { + const id = `agent-${Object.keys(this._agents).length + 1}-${role || 'worker'}`; + if (Object.keys(this._agents).length >= this._SPAWN_LIMIT) { + this.terminal.print(`❌ Agent spawn limit (${this._SPAWN_LIMIT}) reached`, 'error'); + return null; + } + const agent = { + id, + role: role || 'worker', + capabilities: capabilities.length ? capabilities : ['list_files', 'read_file', 'web_search'], + createdAt: new Date().toISOString(), + lastHeartbeat: Date.now(), + status: 'idle', + currentTask: null + }; + this._agents[id] = agent; + // Register in agent registry + const registry = this.terminal.moduleManager.modules['AgentRegistryModule']; + if (registry) registry.registerAgent(['register', id, ...agent.capabilities]); + this.terminal.print(`🤖 Agent '${id}' spawned (role: ${role || 'worker'}, ${agent.capabilities.length} caps)`, 'success'); + return agent; + } + killAgent(id) { + if (!this._agents[id]) return this.terminal.print(`❌ Agent '${id}' not found`, 'error'); + // Revoke any active lease + const leaseModule = this.terminal.moduleManager.modules['LeaseModule']; + if (leaseModule) { + Object.values(leaseModule._leases).forEach(l => { + if (l.workerId === id && l.status === 'active') leaseModule.revokeLease(l.id); + }); + } + // Unregister from registry + const registry = this.terminal.moduleManager.modules['AgentRegistryModule']; + if (registry) registry.unregisterAgent(id); + delete this._agents[id]; + this.terminal.print(`💀 Agent '${id}' killed`, 'warning'); + } + async delegateTask(task) { + if (!task) return this.terminal.print('Usage: orchestrate <task description>', 'error'); + // Find an idle agent or auto-spawn + let agent = Object.values(this._agents).find(a => a.status === 'idle'); + if (!agent) { + if (Object.keys(this._agents).length >= this._SPAWN_LIMIT) { + return this.terminal.print('❌ All agents busy and spawn limit reached', 'error'); + } + this.spawnAgent('worker', ['list_files', 'read_file', 'write_file', 'web_search']); + agent = Object.values(this._agents).find(a => a.status === 'idle'); + } + if (!agent) return this.terminal.print('❌ No available agents', 'error'); + // Create a lease + const leaseModule = this.terminal.moduleManager.modules['LeaseModule']; + const lease = leaseModule ? leaseModule.createLease(['lease-create', agent.id, '60', task]) : null; + // Mark agent as busy + agent.status = 'busy'; + agent.currentTask = task; + agent.leaseId = lease?.id; + // Write task to agent's inbox + if (!this.terminal.fs.readFile(`/agents/${agent.id}/`)) this.terminal.fs.mkdir(`agents/${agent.id}`); + this.terminal.fs.writeFile(`agents/${agent.id}/inbox.json`, JSON.stringify({ + task, + from: 'orchestrator-0', + leaseId: lease?.id, + timestamp: new Date().toISOString(), + ttl_seconds: 60 + })); + this.terminal.print(`🎯 Delegated "${task.substring(0, 60)}" to ${agent.id} (lease: ${lease?.id || 'none'})`, 'info'); + // Execute via AI assistant + try { + const aiModule = this.terminal.moduleManager.modules['WebX11AIAssistantModule']; + if (aiModule && typeof aiModule.chat === 'function') { + const result = await aiModule.chat([`[DELEGATED] ${task}`]); + agent.status = 'completed'; + this.terminal.fs.writeFile(`agents/${agent.id}/outbox.json`, JSON.stringify({ + result: result || 'completed', + status: 'completed', + timestamp: new Date().toISOString() + })); + this.terminal.print(`✅ ${agent.id} completed task`, 'success'); + } else { + this.terminal.print(`ℹ️ AI module not available — task queued for ${agent.id}`, 'info'); + } + } catch (e) { + agent.status = 'failed'; + this.terminal.print(`❌ ${agent.id} failed: ${e.message}`, 'error'); + } + // Return to idle pool + agent.status = 'idle'; + agent.currentTask = null; + agent.lastHeartbeat = Date.now(); + } + _checkHeartbeats() { + const now = Date.now(); + Object.entries(this._agents).forEach(([id, agent]) => { + if (agent.status === 'busy' && now - agent.lastHeartbeat > 15000) { + this.terminal.print(`⚠️ Agent '${id}' unresponsive (${Math.round((now - agent.lastHeartbeat) / 1000)}s since heartbeat)`, 'warning'); + } + }); + } + purgeDeadAgents() { + const now = Date.now(); + let count = 0; + Object.entries(this._agents).forEach(([id, agent]) => { + if (now - agent.lastHeartbeat > 60000) { + this.killAgent(id); + count++; + } + }); + this.terminal.print(`🧹 Purged ${count} dead agents`, count > 0 ? 'warning' : 'info'); + } + showStatus() { + const total = Object.keys(this._agents).length; + const busy = Object.values(this._agents).filter(a => a.status === 'busy').length; + const idle = Object.values(this._agents).filter(a => a.status === 'idle').length; + const failed = Object.values(this._agents).filter(a => a.status === 'failed').length; + this.terminal.print('🧠 Orchestrator Status', 'header'); + this.terminal.print(` Agents: ${total} (${busy} busy, ${idle} idle, ${failed} failed)`, 'info'); + this.terminal.print(` Spawn limit: ${this._SPAWN_LIMIT}`, 'info'); + Object.values(this._agents).forEach(a => { + const task = a.currentTask ? ` (task: ${a.currentTask.substring(0, 40)})` : ''; + this.terminal.print(` ${a.id} — ${a.status}${task}`, 'info'); + }); + } +}
\ No newline at end of file diff --git a/modules/core/module-system.js b/modules/core/module-system.js index e54a5fe..c0dbe14 100644 --- a/modules/core/module-system.js +++ b/modules/core/module-system.js @@ -36,6 +36,13 @@ this.terminal.print(`Memory Usage: ${mem} MB`, 'info'); document.getElementById('header-status').innerText = `Mem: ${mem}MB`; }; + // Refresh memory display on boot + setTimeout(() => { + if (performance.memory) { + const mem = (performance.memory.usedJSHeapSize / 1048576).toFixed(1); + document.getElementById('header-status').innerText = `Mem: ${mem}MB`; + } + }, 500); this.commands['status'] = () => { this.terminal.print(`System: WebX11 v4.2`); this.terminal.print(`Network: ${navigator.onLine ? 'Online' : 'Offline'}`); @@ -223,12 +230,12 @@ } // Categorize all commands const cats = { - files: ['ls','ll','cat','put','rm','mv','cp','mkdir','rmdir','cd','pwd','find','grep','head','tail','sort','wc','touch','tree','du','upload','download','vfs-export','vfs-import','edit','diff'], - ai: ['ai','ask','ai-clear','ai-system','ai-context','ai-allow','ai-deny','ai-ignore','ai-stop','ai-model'], + files: ['ls','ll','cat','put','rm','mv','cp','mkdir','rmdir','cd','pwd','find','grep','head','tail','sort','wc','touch','tree','du','upload','download','vfs-export','vfs-import','edit','diff','trash','trash-dir','ls-trash','empty-trash'], + ai: ['ai','ask','ai-clear','ai-system','ai-context','ai-allow','ai-deny','ai-ignore','ai-stop','ai-model','ai-compress','debug'], search: ['search','s','ddg','duck','fetch','web','extract'], tools: ['calc','cron-every','cron-list','cron-stop','cron-clear','wpm','mindmap','theme','voice','alias','unalias', - 'which','env','date','cal','yes','seq','base64','printf','cut','uniq','hostname','whoami','basename','dirname','sleep','expand','tutorial'], - system: ['help','clear','cls','echo','mem','status','font-size','setfont','smart-prompt','logs','preview','factory-reset','reset-vfs','export','unset','script','set','test'], + 'which','env','date','cal','yes','seq','base64','printf','cut','uniq','hostname','whoami','basename','dirname','sleep','expand','tutorial','gzip','gunzip','encrypt','decrypt','files','session-save','session-load','session-list','session-delete','session-export','session-import'], + system: ['help','clear','cls','echo','mem','status','font-size','setfont','smart-prompt','logs','preview','factory-reset','reset-vfs','export','unset','script','set','test','logrotate','health-check','memory-map-export','commands','check','checkpoint','checkpoint-list','rollback','checkpoint-del'], net: ['server','servers','server-check','server-remove','monitor'], mods: ['modules','listmods','loadmod','removemod','load-local','loadlocal','startup-modules'], ollama: ['ollama','ollamaconnect','ollamadisconnect','ollamamodels','ollamasetmodel','ollamasetproxy','models','model-info','model-pull','model-delete','model-run'], @@ -236,14 +243,15 @@ config: ['config','config-set','config-list','config-delete','config-export','config-reset'], webx: ['webxai','wx','webx','context','contexts','context-new','context-delete','smart-ask','prompt','prompt-save','prompt-list','prompt-delete','prompt-var','prompt-vars'], data: ['my','my-set','my-get','my-list','my-export','my-import','my-clear','export-mods','import-mods','backup','restore','share-mod'], - ext: ['chat','chat-setapi','chat-setmodel','chat-ask','webdav-config','webdav-status','webdav-ls','webdav-push','webdav-pull','webdav-backup','webdav-restore','webdav-backup-list'], + ext: ['chat','chat-setapi','chat-setmodel','chat-ask','webdav-config','webdav-status','webdav-ls','webdav-push','webdav-pull','webdav-backup','webdav-restore','webdav-backup-list','webdav-explorer','webdav-browser'], gui: ['gui','gui-button','gui-input','gui-text','gui-clear','gui-run'], + vault: ['secret-set','secret-get','secret-list','secret-del','secret-lock','secret-unlock','secret-setpin','secret-status'], }; const labels = { files:'📁 Filesystem', ai:'🤖 AI', search:'🔍 Search & Web', tools:'🔧 Tools', system:'⚙️ System', net:'📡 Network', mods:'🧩 Modules', ollama:'🦙 Ollama', skills:'📚 Skills', config:'⚙️ Config', webx:'🌐 WebX', data:'💾 Data', - ext:'🧰 Extras', gui:'🖥️ GUI' + ext:'🧰 Extras', gui:'🖥️ GUI', vault:'🔒 Vault' }; let result = '╔══════════════════════════════════════╗\n║ 🔷 WebX11 v4.2 — All Commands ║\n╚══════════════════════════════════════╝\n\n'; const total = Object.keys(helpMap).length; diff --git a/modules/core/terminal.js b/modules/core/terminal.js index f0ef036..4111f5f 100644 --- a/modules/core/terminal.js +++ b/modules/core/terminal.js @@ -868,10 +868,10 @@ const lowerInput = input.toLowerCase(); // Expanded command lists for autocomplete - const fileArgCommands = ['cat', 'preview', 'rm', 'rmdir', 'edit', 'view', 'cp', 'mv', 'touch', 'put', 'run', 'chmod', 'gzip', 'gunzip', 'encrypt', 'decrypt', 'webdav-backup', 'webdav-restore', 'webdav-upload', 'webdav-download', 'webdav-list']; + const fileArgCommands = ['cat', 'preview', 'rm', 'rmdir', 'edit', 'view', 'cp', 'mv', 'touch', 'put', 'run', 'chmod', 'gzip', 'gunzip', 'encrypt', 'decrypt', 'download', 'webdav-backup', 'webdav-restore', 'webdav-upload', 'webdav-download', 'webdav-list']; const dirArgCommands = ['ls', 'cd', 'mkdir', 'rmdir']; const sessionArgCommands = ['session-load', 'session-delete', 'session-export', 'session-save', 'session-list', 'session-import']; - const pathArgCommands = ['cat', 'preview', 'rm', 'edit', 'view', 'cp', 'mv', 'touch', 'put', 'run', 'chmod', 'gzip', 'gunzip', 'encrypt', 'decrypt', 'webdav-backup', 'webdav-restore', 'webdav-upload', 'webdav-download', 'webdav-list', 'ls', 'cd', 'mkdir', 'rmdir', 'cat', 'preview']; + const pathArgCommands = ['cat', 'preview', 'rm', 'edit', 'view', 'cp', 'mv', 'touch', 'put', 'run', 'chmod', 'gzip', 'gunzip', 'encrypt', 'decrypt', 'download', 'webdav-backup', 'webdav-restore', 'webdav-upload', 'webdav-download', 'webdav-list', 'ls', 'cd', 'mkdir', 'rmdir', 'cat', 'preview']; // Collect commands that take arguments from ext module metadata const extFileCmds = new Set(); @@ -1024,6 +1024,9 @@ this._safeRegister('SecretVault', 'secret vault'); this.reloadPersistedModules(); this._safeRegister('ChatStudioModule', 'chat studio'); + this._safeRegister('AgentRegistryModule', 'agent registry'); + this._safeRegister('LeaseModule', 'lease system'); + this._safeRegister('OrchestratorModule', 'orchestrator'); } _safeRegister(className, label) { let cls; diff --git a/modules/ext/skill-pack-core.js b/modules/ext/skill-pack-core.js index cc8ae88..f731594 100644 --- a/modules/ext/skill-pack-core.js +++ b/modules/ext/skill-pack-core.js @@ -20,37 +20,51 @@ class SkillPackCoreModule { getHelpInfo() { return { - 'skill-pack-core': 'Seed and manage built-in core skills (navigation, memory, search, modules, backup, mindmap, etc.)', + 'skill-pack-core': 'Seed and manage built-in core skills (navigation, memory, search, modules, backup, agents, webdav, security, etc.)', }; } _buildSkills() { const s = (name, desc, tags, cat, steps) => ({ name, description: desc, tags, category: cat, - content: `---\nname: ${name}\ndescription: ${desc}\ntags: [${tags.join(', ')}]\ncategory: ${cat}\n---\n## Steps\n${steps}` + content: '---\nname: ' + name + '\ndescription: ' + desc + '\ntags: [' + tags.join(', ') + ']\ncategory: ' + cat + '\n---\n## Steps\n' + steps + }); + + const sk = (name, desc, tags, cat, content) => ({ + name, description: desc, tags, category: cat, + content: '---\nname: ' + name + '\ndescription: ' + desc + '\ntags: [' + tags.join(', ') + ']\ncategory: ' + cat + '\n---\n' + content }); return [ s('navigating-the-terminal', 'How to explore the WebX11 terminal, find commands, check status, and navigate the filesystem', ['terminal', 'navigation', 'help', 'filesystem'], 'system', `1. **Get an overview of available commands:** - - Call get_help() to get a full command list sorted by category - - Call get_status() to see filesystem state, loaded modules, Ollama status - - For a specific command, use help <command> or check the help map + - Type "help" for a full command list sorted by category + - Type "status" to see filesystem state, loaded modules, Ollama status + - For a specific command, use help <command> + - Type "commands" for a compact list with module source info + - Type "check" for system health including module requirements 2. **Explore the filesystem:** - - Use list_files(path?) to browse directories (omit path for current dir) - - Use read_file(path) to view file contents - - Use get_current_dir to see your working directory - - Use run_command("ls") or run_command("tree") for visual output + - Use ls, ll, tree to browse directories + - Use cd <dir> to navigate, pwd to show current path + - Use cat or edit to view file contents + - Use find or grep to search files + - Use du to see disk usage 3. **Understand system state:** - - run_command("mem") — memory usage - - run_command("status") — network, files, modules, Ollama - - run_command("modules") — module manager GUI - - get_module_status() — loaded modules, URLs, local edits - -4. **When user asks a vague question:** - - First call get_status() and get_help() to understand the current state + - mem — memory usage (auto-shown in header on boot) + - status — network, files, modules, Ollama + - modules — module manager GUI + - health-check — VFS size, modules, logs, trash, skills, tool failures + - checkpoint-list — VFS checkpoint snapshots + +4. **Agent / Orchestration state:** + - agent-list — registered agents (type, capabilities, status) + - orchestrate — list active orchestrated agents + - lease-list — show active leases with TTL + +5. **When user asks a vague question:** + - First run status and help to understand the current state - Then ask clarifying questions based on what you found`), s('memory-and-profile', 'How to use the user profile system, session memory, and pattern tracking for persistent context', ['memory', 'profile', 'session', 'persistence'], 'system', @@ -67,26 +81,36 @@ class SkillPackCoreModule { - session_recent(count) — show last N session summaries - session_stats — aggregate totals across all sessions - Recent sessions auto-load into context on first message + - systemd-style journal: use logs and logrotate commands for persistent logging 3. **Conversation Patterns:** - Pattern tracking runs automatically — frequent topics surface as hints - my-set, my-get, my-list store additional personal data - AI conversation context persists within a session (use ai-clear to reset) + - debug command toggles real-time debug panel with Copy/Export/VFS buttons -4. **When to use:** +4. **Checkpoints (VFS snapshots):** + - checkpoint [name] — snapshot current VFS state + - checkpoint-list — list all checkpoints + - rollback <name> — restore from checkpoint + - checkpoint-del <name> — delete a checkpoint + +5. **When to use:** - At start: call session_recent(3) to see what was worked on before - After identifying user preferences: call user_set to store them - - After complex tasks: call session_save with a brief summary`), + - After complex tasks: call session_save with a brief summary + - Before risky edits: call checkpoint to create a restore point`), s('web-search-and-fetch', 'How to search the web and fetch URL content using the WebX11 proxy API', ['search', 'fetch', 'web', 'proxy', 'api'], 'research', `1. **Search the web:** - - Call web_search(query) — uses /api/search which queries SearXNG + DDG fallback + - Use search <query> or web_search(query) — uses /api/search which queries SearXNG + DDG fallback - Results include: title, url, content snippet, source engine - Always available (same-origin, no CORS issues) - 5-minute server-side cache for repeated queries + - Alternative: use ddg or duck <query> for DuckDuckGo direct, or s <query> as alias 2. **Fetch URL content:** - - Call web_fetch(url) — uses /api/extract which strips HTML, extracts clean text + - Use fetch <url> or web_fetch(url) — uses /api/extract which strips HTML, extracts clean text - Returns: title, content (up to 4000 chars), word_count, extraction method - SSRF protection blocks internal network addresses - 15-minute server-side cache @@ -96,19 +120,21 @@ class SkillPackCoreModule { - search <query> --fetch — fetches content snippets for top 3 results 4. **When to use:** - - User asks about current events, facts, research → call web_search - - User says "read this article" or provides a URL → call web_fetch + - User asks about current events, facts, research → use search <query> + - User says "read this article" or provides a URL → use fetch <url> - For in-depth analysis: search first, then fetch top results`), s('module-management', 'How to view, edit, test, reload modules and manage startup configuration', ['modules', 'editing', 'code', 'self-improvement'], 'dev', `1. **View module source:** - view_module(name) — fetches original source or reads from _mods/ if edited - Supports all loaded modules: SystemModule, FileSystemModule, SearXNGModule, etc. + - Or use modules command for GUI module manager 2. **Edit a module:** - edit_module(name) — copies source to _mods/<name>.js and opens code editor - Editor shows the full source in a textarea - Click "Save & Test" to save and auto-load the edited version + - Checkpoint before editing: checkpoint mod-edit-<name> 3. **Test and reload:** - test_module(name) — loads edited module from _mods/ for live testing @@ -121,8 +147,15 @@ class SkillPackCoreModule { 5. **Manage startup:** - startup-modules — opens the Startup Modules UI to select which modules load - load-local <path> — load a module from VFS or URL without editing + - loadmod <url> — alternative: load module from URL + - removemod <name> — remove a loaded module -6. **When to use:** +6. **Agent modules (Phase 2):** + - module-agent-registry.js — Agent registry with agent-list, agent-register, agent-unregister, agent-status + - module-leases.js — Time-bound contracts with 5s watchdog, auto-expiry + - module-orchestrator.js — Spawn, delegate, kill, purge with heartbeat monitoring + +7. **When to use:** - User reports a bug in a module → view_module, edit_module, test_module - User asks "can you add X feature?" → same workflow - Always save edits before testing. If test fails, the original module stays loaded.`), @@ -142,42 +175,68 @@ class SkillPackCoreModule { - config-export — dump all config as JSON - config-reset — restore defaults (use before factory-reset if you want to save data) -4. **Health checks (run automatically after complex tasks):** +4. **Security level:** + - security-level <0-3> — set security level (0=Permissive, 1=Standard, 2=Paranoid, 3=Audit) + - security — show current security level, blocked count, and stats + - cmd-allow <cmd> — whitelist a command from security checks + - cmd-deny <cmd> — blacklist a command + +5. **Health checks (run automatically after complex tasks):** - Check module loading status: loaded vs total - Check for pending module edits - Check memory usage (high if >100MB) - Check skill count (0 skills after 2+ tool calls → suggest create_skill) + - health-check — full audit: VFS size, modules, logs, trash, skills, tool failures -5. **Font size and display:** +6. **Font size and display:** - font-size <8-32> — set terminal font size, persists across sessions - setfont — alias for font-size -6. **When to use:** - - System is slow or modules fail to load → check check_updates, memory +7. **Checkpoints:** + - checkpoint [name] — snapshot current VFS state + - checkpoint-list — list all checkpoints + - rollback <name> — restore from checkpoint + +8. **When to use:** + - System is slow or modules fail to load → check check_updates, mem - User says "reset everything" → factory-reset --confirm - - User says "clear my files but keep settings" → reset-vfs --confirm`), + - User says "clear my files but keep settings" → reset-vfs --confirm + - User reports security concerns → security-level or cmd-allow/cmd-deny`), s('backup-and-restore', 'How to backup, restore, and export WebX11 data', ['backup', 'restore', 'export', 'import', 'persistence'], 'system', `1. **Backup VFS:** - vfs-export — downloads the entire virtual filesystem as a dated JSON file - backup — creates a full backup including VFS, modules, config (saves to _backups/) -2. **Export/Import personal data:** +2. **WebDAV cloud backup:** + - webdav-backup — push VFS snapshot to WebDAV server (auto-archive) + - webdav-backup-list — list remote WebDAV backups + - webdav-restore <file> — restore VFS from remote WebDAV snapshot + - webdav-push <local> <remote> — push file to WebDAV space + - webdav-pull <remote> <local> — pull file from WebDAV space + - webdav-ls [path] — list remote WebDAV directory contents + - webdav-status — check WebDAV connection and space usage + - webdav-explorer — GUI file browser for WebDAV + - webdav-browser — simpler file listing for WebDAV + - webdav-config — configure WebDAV endpoint and credentials + +3. **Export/Import personal data:** - my-export — prints all my-set data as formatted JSON - my-import <json> — bulk-import personal data from JSON string -3. **Export/Import modules:** +4. **Export/Import modules:** - export-mods — exports module registry as JSON - import-mods <json> — imports modules from JSON backup -4. **Restore:** +5. **Restore:** - restore <json> — restores terminal state from a backup JSON - Session logs are saved to /memory/ automatically on page unload -5. **When to use:** +6. **When to use:** - Before factory-reset: call my-export, vfs-export, export-mods to save data - When switching browsers or devices: export all, then import - - User says "backup my data" or "save my files"`), + - User says "backup my data" or "save my files" + - For cloud backup: use webdav-backup (requires WebDAV server configured)`), s('mindmap-workflow', 'How to create, view, edit, and manage brainstorming mindmaps', ['mindmap', 'brainstorming', 'planning', 'organization'], 'productivity', `1. **Create a new mindmap:** @@ -215,18 +274,19 @@ class SkillPackCoreModule { s('skills-system-guide', 'How to use the skills system: load, create, patch, list, and audit skills for persistent procedural memory', ['skills', 'system', 'meta'], 'system', `1. **Always load skills at task start:** - - At the START of every task, call load_skills(query) to check for stored procedures - - Example: load_skills("search the web") returns matching skill content - - Example: load_skills("edit module") returns the module editing workflow + - At the START of every task, type or call "skill-view <name>" or "skill-list" + - Example: skill-view "module-management" — returns matching skill content + - Example: skill-list system — browse skills by category 2. **Create skills after complex tasks:** - - After any task with 3+ tool calls, call create_skill to save the approach + - After any task with 3+ tool calls, call "skill-create" - Required fields: name (kebab-case), description, content (markdown with YAML frontmatter) - Optional: tags (comma-separated), category (e.g. research, system, dev) - Skills persist in /skills/ as .md files with /skills/index.json for fast lookup + - Skill pack auto-seeds 8 core skills on first boot 3. **Patch skills when they're outdated:** - - If a stored procedure has wrong steps, call patch_skill(name, newContent) + - If a stored procedure has wrong steps, call "skill-patch <name>" - This replaces the entire skill content and updates the index - Do NOT create a duplicate — always patch the existing skill @@ -235,13 +295,139 @@ class SkillPackCoreModule { - Named tool-<name>-workaround (e.g. tool-write_file-workaround) 5. **Audit and maintenance:** - - list_skills(category?) — browse available skills, optionally filter + - skill-list <category?> — browse available skills, optionally filter + - skill-export <name> — download a skill as markdown + - skill-import <json> — bulk import skills + - skill-sync — re-index skills from /skills/ directory - Skills are audited every 20 messages for staleness (empty content, >30 days old) 6. **YAML frontmatter format:** - Each skill file must start with: ---\\nname: <name>\\ndescription: <text>\\ntags: [...]\\ncategory: <cat>\\n---\\n - Steps follow under ## Steps heading - Use numbered lists for sequential steps`), + + sk('agent-orchestration', 'How to use the agent registry, leases, and orchestrator for multi-agent workflows', ['agents', 'orchestration', 'delegation', 'leases'], 'system', +`## Steps +1. **Agent Registry:** + - agent-list — list all registered agents (ID, type, capabilities, status) + - agent-register <type> <capabilities> — register a new agent (capabilities: comma-separated like "code,research") + - agent-unregister <id> — remove an agent + - agent-status <id> — detailed status including uptime, task history + +2. **Leases (time-bound contracts):** + - lease-create <agent-id> <ttl-seconds> — create a lease with watchdog timer + - lease-list — show active leases with remaining TTL + - lease-revoke <lease-id> — terminate a lease early + - lease-renew <lease-id> <ttl-seconds> — extend a lease + +3. **Orchestrator (spawn, delegate, kill):** + - orchestrator-spawn <type> <capabilities> — spawn a new agent automatically (registers + creates lease) + - orchestrator-kill <agent-id> — kill an agent (revokes lease + unregisters) + - orchestrate — list all active agents with their orchestrator state + - orchestrator-purge — kill all idle/expired agents + - orchestrator-status — full system status: active, idle, dead agents + +4. **AI tool integration:** + - AI tools list_agents, delegate_task, spawn_agent are available in the AI system prompt + - The AI can auto-spawn worker agents for sub-tasks + - Each agent gets its own VFS workspace at /agents/<agent-id>/ + +5. **When to use:** + - User needs parallel task execution → orchestrator-spawn coder + researcher + - Time-bound automation → lease-create with short TTL + - System cleanup → orchestrator-purge to kill stale agents`), + + sk('debug-panel-and-logs', 'How to use the debug panel, session logging, and terminal diagnostics', ['debug', 'logging', 'diagnostics', 'troubleshooting'], 'system', +`## Steps +1. **Toggle debug panel:** + - debug — opens real-time debug panel showing last AI tool call, response time, and context + - Debug panel includes Copy, Export, and Write to VFS buttons + - Export: saves tool call log as timestamped JSON to downloads + - Write to VFS: appends debug data to /debug.log (dedup by turn ID) + +2. **Session logging:** + - logs [n] [LEVEL] — view last n log entries (optionally filtered by INFO/WARN/ERROR) + - logrotate — rotate current.log to dated archive (auto-cleans old logs beyond 10) + - Logs stored in /logs/ directory: current.log + dated archives + - Health check runs automatically after complex tasks + +3. **Diagnostic commands:** + - health-check — full audit: VFS size, modules, logs, trash, skills + - check — quick system health including module requirement status + - mem — real-time memory usage (also in header) + - status — network, files, modules, Ollama connectivity + +4. **System journals:** + - debug.log — appended by debug panel VFS button (dedup by turn ID) + - current.log — system activity log with rotation + - Auto-save: terminal state auto-saves every 60s + +5. **When to use:** + - AI is behaving unexpectedly → debug to see tool calls + - User reports errors → logs 50 ERROR to see error entries + - Memory concerns → mem to check, health-check for full audit`), + + sk('webdav-operations', 'How to use WebDAV cloud storage for backup, file sync, and remote file access', ['webdav', 'cloud', 'backup', 'sync', 'storage'], 'system', +`## Steps +1. **Configuration:** + - webdav-config — configure WebDAV endpoint URL and credentials + - Connection auto-tested on config; webdav-status shows connection health + - Uses per-user token stored in .token file on the server side + +2. **File operations:** + - webdav-ls [path] — list remote directory contents + - webdav-push <local-path> <remote-path> — upload file to WebDAV + - webdav-pull <remote-path> [local-path] — download file from WebDAV + - webdav-explorer — GUI file browser for WebDAV + - webdav-browser — CLI file browser for WebDAV + +3. **Backup & Restore:** + - webdav-backup — push full VFS snapshot to WebDAV (creates /backups/newos4-<date>.json) + - webdav-backup-list — list all remote backup snapshots + - webdav-restore <filename> — restore VFS from a remote backup snapshot + +4. **Status & Diagnostics:** + - webdav-status — connection status, last sync, space information + - If uploads fail: check webdav-config, ensure .token file is valid + - WebDAV runs on the same server (nginx with WebDAV module), no external dependency + +5. **When to use:** + - Before factory-reset: webdav-backup to store VFS remotely + - Cross-browser: webdav-push/pull to move files between instances + - User says "cloud backup" or "remote save" → webdav-backup`), + + sk('security-and-safety', 'How to use WebX11 security levels, command blocking, and safety features', ['security', 'safety', 'blocking', 'permissions'], 'system', +`## Steps +1. **Security levels:** + - security-level <0-3> — set security level: + 0 = Permissive (no blocks, no confirms — anything runs) + 1 = Standard (unknown cmds → AI route, destructive → confirm, system → block) + 2 = Paranoid (unknown cmds blocked entirely, destructive blocked) + 3 = Audit (everything allowed, everything logged) + - Default: level 1 + - security — show current level, blocked count, and stats + +2. **Command control:** + - cmd-allow <cmd> — whitelist a command from security checks + - cmd-deny <cmd> — blacklist/block a command entirely + - whitelist built-in: help, clear, ls, cat, pwd, whoami, df, free, top, ps, echo, date, env, history + +3. **What gets blocked at each level:** + - Unknown commands: Level 1 → AI routed, Level 2+ → blocked + - Destructive commands (delete, write, save, format, etc.): Level 1 → confirmation, Level 2+ → blocked + - System commands (reboot, shutdown, format, kill): Level 1+ → blocked + - Suspicious flags (--execute, --eval, --shell): Level 1+ → blocked + +4. **Safety checklist:** + - Check security: security shows your current level + - Before running risky commands: create a checkpoint first + - For temporary relax: security-level 0, then restore to 1 + - For total lockdown before leaving: security-level 2 + +5. **When to use:** + - User wants to lock down the terminal → security-level 2 + - User wants to allow a blocked command → cmd-allow <cmd> + - Debugging strange behavior → verify security-level (user may have set it higher)`) ]; } registerCommands() { @@ -264,8 +450,9 @@ class SkillPackCoreModule { console.log('[SkillPackCore] existing index keys:', Object.keys(index)); let seeded = 0; for (const skill of this._skills) { - if (!index[skill.name]) { - fs.writeFile(`/skills/${skill.name}.md`, skill.content); + const existing = index[skill.name]; + if (!existing) { + fs.writeFile('/skills/' + skill.name + '.md', skill.content); index[skill.name] = { name: skill.name, description: skill.description, @@ -275,14 +462,25 @@ class SkillPackCoreModule { updated: new Date().toISOString() }; seeded++; + } else { + // Update existing skill content on fresh load (cache-bust version bump triggers this) + fs.writeFile('/skills/' + skill.name + '.md', skill.content); + index[skill.name] = { + ...existing, + description: skill.description, + tags: skill.tags, + category: skill.category, + updated: new Date().toISOString() + }; } } if (seeded > 0) { - fs.fsWriteJSON('/skills/index.json', index); + this.terminal.print('📚 Seeded ' + seeded + ' new skill(s)', 'success'); } + fs.fsWriteJSON('/skills/index.json', index); this._seeded = true; } } if (typeof window.webx11 !== 'undefined' && window.webx11) { webx11.moduleManager.registerModule(new SkillPackCoreModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/skill-pack-core.js'); -} +}
\ No newline at end of file diff --git a/modules/ext/webdav.js b/modules/ext/webdav.js index b4dfa6f..eea513b 100644 --- a/modules/ext/webdav.js +++ b/modules/ext/webdav.js @@ -46,8 +46,11 @@ class WebDAVModule { 'webdav-backup': 'Backup entire VFS to WebDAV as a dated JSON snapshot', 'webdav-restore': 'Restore VFS from a WebDAV backup file', 'webdav-backup-list': 'List available backup files on WebDAV', - 'webdav-explorer': 'webdav-explorer [path] — Interactive remote file browser', - 'webdav-browser': 'webdav-browser [path] — Visual WebDAV file explorer modal', + 'webdav-browser': 'webdav-browser [path] — Modern WebDAV file-tree browser with bulk operations', + 'webdav-rm': 'webdav-rm <path> — Delete a remote file or directory', + 'webdav-mkdir': 'webdav-mkdir <path> — Create a remote directory', + 'webdav-rename': 'webdav-rename <old> <new> — Rename or move a remote file/dir', + 'webdav-move': 'webdav-move <old> <new> — Alias for webdav-rename', }; } @@ -127,7 +130,11 @@ class WebDAVModule { } async _listDir(path) { - // Try PROPFIND first (works with WebDAV servers), fallback to nginx autoindex + // For WebX11 spaces, skip PROPFIND (nginx returns 302) and use __list__ directly + if (this._spaceName) { + return this._listDirViaAutoIndex(path); + } + // Non-nginx: try PROPFIND first, fallback to autoindex try { const response = await this._webdavRequest('PROPFIND', path, { headers: { 'Depth': '1' } @@ -137,15 +144,24 @@ class WebDAVModule { return this._parsePropfind(text); } } catch (e) { /* PROPFIND failed, try autoindex */ } - // Fallback: use nginx __list__ endpoint return this._listDirViaAutoIndex(path); } async _listDirViaAutoIndex(path) { - const baseUrl = this.config.url.replace(/\/+$/, '') + '/'; - const urlPath = path ? (path.startsWith('/') ? path : '/' + path) : '/'; - // Use __list__ for directory listing - const listUrl = baseUrl.replace(/\/spaces\/([^/]+)\//, '/spaces/$1/__list__/') + urlPath.replace(/^\//, ''); + if (!this._spaceName) { + throw new Error('WebDAV: cannot use __list__ without space name'); + } + // Derive host from configured URL to support different environments + let host = 'https://webx11.duckdns.org'; + if (this.config && this.config.url) { + try { + const u = new URL(this.config.url); + host = u.origin; + } catch (e) {} + } + const cleanPath = path ? (path.startsWith('/') ? path : '/' + path) : '/'; + // Avoid double slash when path starts with / + const listUrl = host + '/spaces/' + this._spaceName + '/__list__' + (cleanPath === '/' ? '' : cleanPath.replace(/\/{2,}/g, '/')); try { const resp = await fetch(listUrl, { method: 'GET' }); if (!resp.ok) throw new Error('Autoindex failed: HTTP ' + resp.status); @@ -158,15 +174,14 @@ class WebDAVModule { _parseAutoindex(html) { const entries = []; - // Parse nginx autoindex HTML format - const linkRegex = /<a href="([^"]+)">([^<]+)<\/a>\s+(\d{2}-[A-Za-z]{3}-\d{4}\s+\d{2}:\d{2})\s+([\d\w.-]+)/g; + // Parse nginx autoindex HTML format — flexible regex that doesn't depend on date locale + const linkRegex = /<a href="([^"]+)">([^<]+)<\/a>\s+([\w]{2}-[\w]{3}-\d{4}\s+\d{2}:\d{2})\s+([\d\w.-]+)/g; let match; while ((match = linkRegex.exec(html)) !== null) { const href = match[1]; const name = match[2].replace(/\/$/, ''); const sizeStr = match[4]; - if (name === '..' || name === '../') continue; - if (name === '__list__') continue; + if (name === '..' || name === '../' || name === '__list__') continue; const isDir = href.endsWith('/'); let size = 0; if (!isDir && sizeStr !== '-') { @@ -181,22 +196,40 @@ class WebDAVModule { } // Fallback: if regex failed, try simpler parse if (entries.length === 0) { - const simpleRegex = /<a href="([^"]+)">/g; + // Try broadest: extract all hrefs + const broadRegex = /<a href="([^"]+)"[^>]*>/g; const names = []; - while ((match = simpleRegex.exec(html)) !== null) { + while ((match = broadRegex.exec(html)) !== null) { const href = match[1]; if (href === '../' || href === '/' || href === './') continue; if (href.startsWith('?')) continue; names.push(href); } - for (const href of names) { - const isDir = href.endsWith('/'); - entries.push({ - name: decodeURIComponent(href.replace(/\/$/, '').split('/').pop() || href), - type: isDir ? 'dir' : 'file', - size: 0, - href: href - }); + if (names.length > 0) { + for (const href of names) { + const isDir = href.endsWith('/'); + entries.push({ + name: decodeURIComponent(href.replace(/\/$/, '').split('/').pop() || href), + type: isDir ? 'dir' : 'file', + size: 0, + href: href + }); + } + } else { + // Last resort: match any <a href="..."> + const lastRegex = /<a href="([^"]+)"/g; + while ((match = lastRegex.exec(html)) !== null) { + const href = match[1]; + if (href === '../' || href === '/' || href === './') continue; + if (href.startsWith('?')) continue; + const isDir = href.endsWith('/'); + entries.push({ + name: decodeURIComponent(href.replace(/\/$/, '').split('/').pop() || href), + type: isDir ? 'dir' : 'file', + size: 0, + href: href + }); + } } } return entries; @@ -529,16 +562,27 @@ class WebDAVModule { try { await self._ensureDir('backups'); // Build structured export: root + currentPath + aliases + moduleRegistry - const exportData = { - type: 'webx11-backup', - version: 2, - savedAt: new Date().toISOString(), - root: self.terminal.fs.root, - currentPath: self.terminal.fs.currentPath, - aliases: self.terminal.moduleManager.aliases || {}, - moduleRegistry: self.terminal.moduleManager.moduleRegistry || [] - }; + let exportData; + try { + exportData = { + type: 'webx11-backup', + version: 2, + savedAt: new Date().toISOString(), + root: self.terminal.fs.root, + currentPath: self.terminal.fs.currentPath, + aliases: self.terminal.moduleManager.aliases || {}, + moduleRegistry: self.terminal.moduleManager.moduleRegistry || [] + }; + } catch (e) { + self.terminal.print('Failed to read VFS: ' + e.message, 'error'); + return; + } const serialized = JSON.stringify(exportData, null, 2); + // Size check: abort if >5MB + if (serialized.length > 5 * 1024 * 1024) { + self.terminal.print('Backup too large: ' + self._formatSize(serialized.length) + ' (>5MB limit). Try reducing VFS size first.', 'error'); + return; + } // Gzip payload let payload = serialized; let isGzipped = false; @@ -550,7 +594,9 @@ class WebDAVModule { const buf = await resp.arrayBuffer(); payload = new Uint8Array(buf); isGzipped = true; - } catch (e) { /* fallback to plain */ } + } catch (e) { self.terminal.print('Gzip unavailable, falling back to plain JSON', 'info'); } + } else { + self.terminal.print('CompressionStream not supported, saving as plain JSON', 'info'); } // Encrypt if requested if (encryptPass) { @@ -671,37 +717,74 @@ class WebDAVModule { } }; - // --- webdav-browser: visual modal file browser --- + // --- webdav-browser: modern visual file browser --- this.commands['webdav-browser'] = async (args) => { const startPath = args[0] || self._browsingPath || '/'; await self.openWebdavBrowser(startPath); }; - // --- webdav-explorer: interactive file browser via WebDAV --- - this.commands['webdav-explorer'] = async (args) => { - const startPath = args[0] || '/'; + // --- webdav-rm: delete remote file/dir --- + this.commands['webdav-rm'] = async (args) => { + if (!args[0]) { + self.terminal.print('Usage: webdav-rm <remote-path>', 'warning'); + return; + } try { - const entries = await self._listDir(startPath); - self.terminal.print('📁 WebDAV: ' + startPath, 'info'); - if (entries.length === 0) { - self.terminal.print('(empty)', 'info'); - return; + const ok = await self._deleteFile(args[0]); + if (ok) { + self.terminal.print('Deleted: ' + args[0], 'success'); + } else { + self.terminal.print('Delete failed: resource may not exist', 'error'); } - entries.forEach(e => { - const icon = e.type === 'dir' ? '📁' : '📄'; - const size = e.type === 'file' ? ' (' + self._formatSize(e.size) + ')' : '/'; - self.terminal.print(icon + ' ' + e.name + (e.type === 'dir' ? '/' : '') + size); + } catch (e) { + self.terminal.print('Delete error: ' + e.message, 'error'); + } + }; + + // --- webdav-mkdir: create remote directory --- + this.commands['webdav-mkdir'] = async (args) => { + if (!args[0]) { + self.terminal.print('Usage: webdav-mkdir <remote-path>', 'warning'); + return; + } + try { + const ok = await self._ensureDir(args[0]); + if (ok) { + self.terminal.print('Created directory: ' + args[0], 'success'); + } else { + self.terminal.print('Failed to create directory', 'error'); + } + } catch (e) { + self.terminal.print('Mkdir error: ' + e.message, 'error'); + } + }; + + // --- webdav-rename: rename/move remote file or dir (MOVE method) --- + this.commands['webdav-rename'] = async (args) => { + if (!args[0] || !args[1]) { + self.terminal.print('Usage: webdav-rename <old-path> <new-path>', 'warning'); + self.terminal.print('Renames or moves a file/directory using WebDAV MOVE', 'info'); + return; + } + try { + const resp = await self._webdavRequest('MOVE', args[0], { + headers: { 'Destination': args[1] } }); - self.terminal.print('---'); - self.terminal.print('↳ Navigate: webdav-explorer <dir> | Pull: webdav-pull <file>', 'text-muted'); - self.terminal.print(entries.length + ' item(s)', 'text-muted'); + if (resp.ok || resp.status === 201 || resp.status === 204) { + self.terminal.print('Renamed: ' + args[0] + ' → ' + args[1], 'success'); + } else { + self.terminal.print('Rename failed: HTTP ' + resp.status, 'error'); + } } catch (e) { - self.terminal.print('WebDAV error: ' + e.message, 'error'); + self.terminal.print('Rename error: ' + e.message, 'error'); } }; + + // --- webdav-move: alias for webdav-rename --- + this.commands['webdav-move'] = this.commands['webdav-rename']; } - /* ── Visual WebDAV Browser Modal ── */ + /* ── Modern WebDAV File-Tree Browser with Bulk Ops ── */ async openWebdavBrowser(path) { if (!this.config || !this.config.url) { @@ -710,6 +793,7 @@ class WebDAVModule { } this._browsingPath = path; const self = this; + this._selected = {}; // Remove existing modal if any const existing = document.getElementById('webdav-browser-modal'); @@ -724,6 +808,12 @@ class WebDAVModule { errorMsg = e.message; } + // Sort: dirs first, then files, alphabetically + entries.sort(function(a, b) { + if (a.type !== b.type) return a.type === 'dir' ? -1 : 1; + return a.name.localeCompare(b.name); + }); + // Build modal const overlay = document.createElement('div'); overlay.id = 'webdav-browser-modal'; @@ -733,73 +823,237 @@ class WebDAVModule { const content = document.createElement('div'); content.className = 'modal-content'; - content.style.cssText = 'max-width:650px; max-height:80vh; width:92%;'; + content.style.cssText = 'max-width:750px; max-height:85vh; width:94%;'; // Header const header = document.createElement('div'); header.className = 'modal-header'; - header.innerHTML = '<span>📁 WebDAV Browser</span><span style="cursor:pointer" onclick="var m=document.getElementById(\'webdav-browser-modal\');if(m)m.remove();var t=window.webx11;if(t&&t.inputField)t.inputField.focus();">✕</span>'; + header.innerHTML = '<span>📁 WebDAV File Tree</span>' + + '<span style="display:flex;gap:6px;align-items:center">' + + '<span id="sel-count" style="font-size:0.8em;color:var(--text-muted)"></span>' + + '<span style="cursor:pointer" onclick="var m=document.getElementById(\'webdav-browser-modal\');if(m)m.remove();var t=window.webx11;if(t&&t.inputField)t.inputField.focus();">✕</span></span>'; content.appendChild(header); // Body const body = document.createElement('div'); body.className = 'modal-body'; - body.style.cssText = 'padding:8px; overflow-y:auto; flex-grow:1;'; + body.style.cssText = 'padding:0; overflow-y:auto; flex-grow:1;'; if (errorMsg) { const errDiv = document.createElement('div'); errDiv.style.cssText = 'color:var(--error-color); padding:16px; text-align:center;'; errDiv.textContent = '❌ ' + errorMsg; body.appendChild(errDiv); - } else if (entries.length === 0) { - const emptyDiv = document.createElement('div'); - emptyDiv.className = 'text-center'; - emptyDiv.style.cssText = 'padding:20px; color:var(--text-muted);'; - emptyDiv.textContent = '(empty) — upload files using the button below'; - body.appendChild(emptyDiv); } else { - // Sort: dirs first, then files, alphabetically - entries.sort(function(a, b) { - if (a.type !== b.type) return a.type === 'dir' ? -1 : 1; - return a.name.localeCompare(b.name); - }); + // Select-all row + const selectAllRow = document.createElement('div'); + selectAllRow.style.cssText = 'display:flex;align-items:center;padding:6px 10px;border-bottom:1px solid #222;font-size:0.8em;color:var(--text-muted);gap:8px;'; + const selAllCheck = document.createElement('input'); + selAllCheck.type = 'checkbox'; + selAllCheck.style.cssText = 'accent-color:var(--success-color);width:16px;height:16px;cursor:pointer;'; + selAllCheck.onchange = function() { + const checked = this.checked; + entries.forEach(function(e) { self._selected[e.name] = checked; }); + document.querySelectorAll('.wdav-checkbox').forEach(function(c) { c.checked = checked; }); + self._updateSelectionCount(); + }; + selectAllRow.appendChild(selAllCheck); + selectAllRow.appendChild(document.createTextNode('Select All (' + entries.length + ' items)')); + body.appendChild(selectAllRow); - var frag = document.createDocumentFragment(); + // File rows + const listContainer = document.createElement('div'); + let rowIndex = 0; entries.forEach(function(entry) { - var item = document.createElement('div'); - item.className = entry.type === 'dir' ? 'file-item dir' : 'file-item'; - item.style.cssText = 'display:flex; justify-content:space-between; align-items:center; padding:8px; border-bottom:1px solid #111; cursor:pointer; min-height:36px;'; - item.onmouseover = function() { this.style.background = 'var(--bg-tertiary)'; }; - item.onmouseout = function() { this.style.background = ''; }; + var row = document.createElement('div'); + row.className = 'wdav-row'; + row.style.cssText = 'display:flex;align-items:center;padding:7px 10px;border-bottom:1px solid #1a1a1a;cursor:default;min-height:38px;gap:8px;'; + row.onmouseover = function() { this.style.background = 'var(--bg-tertiary)'; }; + row.onmouseout = function() { this.style.background = ''; }; + row.dataset.index = rowIndex++; + + // Checkbox + var cb = document.createElement('input'); + cb.type = 'checkbox'; + cb.className = 'wdav-checkbox'; + cb.style.cssText = 'accent-color:var(--success-color);width:15px;height:15px;cursor:pointer;flex-shrink:0;'; + cb.onchange = function(e) { + self._selected[entry.name] = this.checked; + self._updateSelectionCount(); + e.stopPropagation(); + }; + row.appendChild(cb); + // Icon + var iconSpan = document.createElement('span'); + iconSpan.textContent = entry.type === 'dir' ? '📁' : '📄'; + iconSpan.style.cssText = 'flex-shrink:0;font-size:1.1em;'; + + // Filename (editable on dblclick) var nameSpan = document.createElement('span'); - var icon = entry.type === 'dir' ? '📁' : '📄'; - nameSpan.textContent = icon + ' ' + entry.name + (entry.type === 'dir' ? '/' : ''); + nameSpan.textContent = entry.name + (entry.type === 'dir' ? '/' : ''); + nameSpan.style.cssText = 'flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:0.9em;cursor:pointer;'; + nameSpan.title = entry.name; + nameSpan.ondblclick = function(e) { + e.stopPropagation(); + var input = document.createElement('input'); + input.type = 'text'; + input.value = entry.name; + input.style.cssText = 'width:100%;background:#1a1a1a;border:1px solid var(--success-color);color:var(--text-color);padding:2px 6px;border-radius:3px;font-size:0.9em;'; + input.onblur = function() { + var newName = this.value.trim(); + if (newName && newName !== entry.name) { + var parentPath = path.replace(/\/+$/, '') || ''; + var oldFull = parentPath + '/' + entry.name; + var newFull = parentPath + '/' + newName; + self._webdavRequest('MOVE', oldFull, { headers: { 'Destination': newFull } }) + .then(function(r) { + if (r.ok || r.status === 201 || r.status === 204) { + self.terminal.print('Renamed: ' + entry.name + ' → ' + newName, 'success'); + self.openWebdavBrowser(path); + } else { + self.terminal.print('Rename failed: HTTP ' + r.status, 'error'); + nameSpan.textContent = entry.name; + } + }); + } else { + nameSpan.textContent = entry.name; + } + }; + input.onkeydown = function(ev) { + if (ev.key === 'Enter') this.blur(); + if (ev.key === 'Escape') { nameSpan.textContent = entry.name; } + }; + nameSpan.textContent = ''; + nameSpan.appendChild(input); + input.focus(); + input.select(); + }; + // Click = navigate dir / preview file + if (entry.type === 'dir') { + row.onclick = function(e) { + if (e.target.type === 'checkbox') return; + self.openWebdavBrowser(entry.href); + }; + } else { + row.onclick = function(e) { + if (e.target.type === 'checkbox') return; + self.previewWebdavFile(entry); + }; + } + // Size var sizeSpan = document.createElement('span'); - sizeSpan.style.cssText = 'font-size:0.8em; color:var(--text-muted);'; + sizeSpan.style.cssText = 'font-size:0.75em;color:var(--text-muted);flex-shrink:0;min-width:60px;text-align:right;'; if (entry.type === 'file') { sizeSpan.textContent = self._formatSize(entry.size); } - item.appendChild(nameSpan); - item.appendChild(sizeSpan); + // Context menu on right-click + row.oncontextmenu = function(e) { + e.preventDefault(); + self._showContextMenu(e, entry, path); + }; - if (entry.type === 'dir') { - item.onclick = function() { self.openWebdavBrowser(entry.href); }; - } else { - item.onclick = function() { self.previewWebdavFile(entry); }; - // Right-click or context menu could download — but for simplicity, long tap or regular click previews - // Double-click to download - item.ondblclick = function() { self.downloadFromWebdav(entry); }; - } - frag.appendChild(item); + row.appendChild(iconSpan); + row.appendChild(nameSpan); + row.appendChild(sizeSpan); + listContainer.appendChild(row); }); - body.appendChild(frag); + body.appendChild(listContainer); + + // Keyboard navigation + body.tabIndex = -1; + body.style.outline = 'none'; + var focusIdx = 0; + body.onkeydown = function(e) { + var rows = listContainer.querySelectorAll('.wdav-row'); + if (e.key === 'ArrowDown' || e.key === 'j') { + e.preventDefault(); + focusIdx = Math.min(focusIdx + 1, rows.length - 1); + rows[focusIdx].scrollIntoView({ block: 'nearest' }); + rows.forEach(function(r, i) { r.style.borderLeft = i === focusIdx ? '2px solid var(--success-color)' : ''; }); + } else if (e.key === 'ArrowUp' || e.key === 'k') { + e.preventDefault(); + focusIdx = Math.max(focusIdx - 1, 0); + rows[focusIdx].scrollIntoView({ block: 'nearest' }); + rows.forEach(function(r, i) { r.style.borderLeft = i === focusIdx ? '2px solid var(--success-color)' : ''; }); + } else if (e.key === ' ') { + e.preventDefault(); + var cb = rows[focusIdx]?.querySelector('.wdav-checkbox'); + if (cb) { cb.checked = !cb.checked; cb.onchange({ stopPropagation: function(){} }); } + } else if (e.key === 'Enter') { + e.preventDefault(); + rows[focusIdx]?.click(); + } + }; + setTimeout(function() { body.focus(); }, 100); } content.appendChild(body); + // Bulk action bar + const bulkBar = document.createElement('div'); + bulkBar.id = 'wdav-bulk-bar'; + bulkBar.style.cssText = 'display:none;padding:8px 10px;border-top:1px solid var(--success-color);background:var(--bg-tertiary);gap:6px;align-items:center;flex-wrap:wrap;'; + bulkBar.innerHTML = '<span id="bulk-count" style="font-size:0.85em;color:var(--text-color);flex:1;">0 selected</span>' + + '<button class="btn" id="bulk-delete" style="font-size:0.8em;padding:3px 10px;">🗑 Delete</button>' + + '<button class="btn" id="bulk-move" style="font-size:0.8em;padding:3px 10px;">📦 Move</button>' + + '<button class="btn" id="bulk-download" style="font-size:0.8em;padding:3px 10px;">⬇ Download</button>'; + self._updateSelectionCount = function() { + var count = Object.values(self._selected).filter(Boolean).length; + var el = document.getElementById('sel-count'); + if (el) el.textContent = count > 0 ? count + ' selected' : ''; + var bar = document.getElementById('wdav-bulk-bar'); + var cnt = document.getElementById('bulk-count'); + if (bar) bar.style.display = count > 0 ? 'flex' : 'none'; + if (cnt) cnt.textContent = count + ' selected'; + }; + + // Wire bulk actions + setTimeout(function() { + document.getElementById('bulk-delete')?.addEventListener('click', function() { + var names = Object.entries(self._selected).filter(function(e) { return e[1]; }).map(function(e) { return e[0]; }); + if (!names.length || !confirm('Delete ' + names.length + ' item(s) from ' + path + '?')) return; + names.forEach(function(name) { + var fullPath = (path.replace(/\/+$/, '') || '') + '/' + name; + self._deleteFile(fullPath).then(function(ok) { + if (ok) self.terminal.print('Deleted: ' + name, 'success'); + else self.terminal.print('Delete failed: ' + name, 'error'); + }); + }); + setTimeout(function() { self.openWebdavBrowser(path); }, 500); + }); + document.getElementById('bulk-move')?.addEventListener('click', function() { + var names = Object.entries(self._selected).filter(function(e) { return e[1]; }).map(function(e) { return e[0]; }); + if (!names.length) return; + var target = prompt('Move ' + names.length + ' item(s) to directory:', '/'); + if (!target) return; + target = target.replace(/\/+$/, ''); + names.forEach(function(name) { + var oldFull = (path.replace(/\/+$/, '') || '') + '/' + name; + var newFull = target + '/' + name; + self._webdavRequest('MOVE', oldFull, { headers: { 'Destination': newFull } }) + .then(function(r) { + if (r.ok || r.status === 201 || r.status === 204) self.terminal.print('Moved: ' + name, 'success'); + else self.terminal.print('Move failed: ' + name + ' (HTTP ' + r.status + ')', 'error'); + }); + }); + setTimeout(function() { self.openWebdavBrowser(path); }, 500); + }); + document.getElementById('bulk-download')?.addEventListener('click', function() { + var names = Object.entries(self._selected).filter(function(e) { return e[1]; }).map(function(e) { return e[0]; }); + names.forEach(function(name) { + var fullPath = (path.replace(/\/+$/, '') || '') + '/' + name; + // Find entry from entries + var entry = entries.find(function(e) { return e.name === name; }); + if (entry && entry.type === 'file') self.downloadFromWebdav(entry); + }); + }); + }, 50); + + content.appendChild(bulkBar); + // Footer with breadcrumb + buttons const footer = document.createElement('div'); footer.className = 'modal-footer'; @@ -815,17 +1069,30 @@ class WebDAVModule { for (var i = 0; i < parts.length; i++) { cumulative += '/' + parts[i]; var seg = parts[i]; + breadHtml += ' <span style="color:var(--text-muted);font-size:0.7em;">▸</span> '; breadHtml += '<span style="cursor:pointer;text-decoration:underline;" onclick="var m=document.getElementById(\'webdav-browser-modal\');if(m)m.__nav(\'' + cumulative.replace(/'/g, "\\'") + '/\');return false;">' + seg + '</span>'; - if (i < parts.length - 1) breadHtml += ' ▸ '; } breadDiv.innerHTML = breadHtml; - // Attach nav function to modal element for breadcrumb clicks overlay.__nav = function(p) { self.openWebdavBrowser(p); }; // Button group var btnGroup = document.createElement('div'); btnGroup.style.cssText = 'display:flex; gap:4px; flex-shrink:0;'; + var mkdirBtn = document.createElement('button'); + mkdirBtn.className = 'btn'; + mkdirBtn.textContent = '📁+'; + mkdirBtn.title = 'New Directory'; + mkdirBtn.onclick = function() { + var name = prompt('Directory name:'); + if (name) { + self._ensureDir((path.replace(/\/+$/, '') || '') + '/' + name).then(function(ok) { + if (ok) { self.terminal.print('Created: ' + name, 'success'); self.openWebdavBrowser(path); } + else self.terminal.print('Failed to create directory', 'error'); + }); + } + }; + var uploadBtn = document.createElement('button'); uploadBtn.className = 'btn'; uploadBtn.textContent = '⬆ Upload'; @@ -842,6 +1109,7 @@ class WebDAVModule { closeBtn.textContent = 'Close'; closeBtn.onclick = function() { self.closeWebdavBrowser(); }; + btnGroup.appendChild(mkdirBtn); btnGroup.appendChild(uploadBtn); btnGroup.appendChild(refreshBtn); btnGroup.appendChild(closeBtn); @@ -853,7 +1121,71 @@ class WebDAVModule { overlay.appendChild(content); document.body.appendChild(overlay); - this.terminal.print('📂 Opened WebDAV browser: ' + path, 'info'); + this.terminal.print('📂 Opened WebDAV file browser: ' + path, 'info'); + } + + _showContextMenu(e, entry, currentPath) { + var existing = document.getElementById('wdav-context-menu'); + if (existing) existing.remove(); + + var menu = document.createElement('div'); + menu.id = 'wdav-context-menu'; + menu.style.cssText = 'position:fixed;left:' + e.clientX + 'px;top:' + e.clientY + 'px;background:#111;border:1px solid #333;border-radius:6px;padding:4px 0;z-index:1001;min-width:160px;box-shadow:0 4px 12px rgba(0,0,0,0.5);'; + menu.onclick = function() { this.remove(); }; + + var items = []; + if (entry.type === 'file') { + items.push({ label: '📄 Preview', fn: function() { this.previewWebdavFile(entry); }.bind(this) }); + items.push({ label: '⬇ Download', fn: function() { this.downloadFromWebdav(entry); }.bind(this) }); + items.push({ label: '📋 Copy Path', fn: function() { + var full = (currentPath.replace(/\/+$/, '') || '') + '/' + entry.name; + navigator.clipboard.writeText(full).then(function() { this.terminal.print('Path copied: ' + full, 'success'); }.bind(this)); + }.bind(this) }); + } else { + items.push({ label: '📂 Open', fn: function() { this.openWebdavBrowser(entry.href); }.bind(this) }); + items.push({ label: '📋 Copy Path', fn: function() { + var full = (currentPath.replace(/\/+$/, '') || '') + '/' + entry.name + '/'; + navigator.clipboard.writeText(full).then(function() { this.terminal.print('Path copied: ' + full, 'success'); }.bind(this)); + }.bind(this) }); + } + items.push({ label: '✏️ Rename', fn: function() { + var newName = prompt('Rename "' + entry.name + '" to:'); + if (newName && newName !== entry.name) { + var oldFull = (currentPath.replace(/\/+$/, '') || '') + '/' + entry.name; + var newFull = (currentPath.replace(/\/+$/, '') || '') + '/' + newName; + this._webdavRequest('MOVE', oldFull, { headers: { 'Destination': newFull } }).then(function(r) { + if (r.ok || r.status === 201 || r.status === 204) { + this.terminal.print('Renamed: ' + entry.name + ' → ' + newName, 'success'); + this.openWebdavBrowser(currentPath); + } else { this.terminal.print('Rename failed: HTTP ' + r.status, 'error'); } + }.bind(this)); + } + }.bind(this) }); + items.push({ label: '🗑 Delete', fn: function() { + if (!confirm('Delete ' + entry.name + '?')) return; + var full = (currentPath.replace(/\/+$/, '') || '') + '/' + entry.name; + this._deleteFile(full).then(function(ok) { + if (ok) { this.terminal.print('Deleted: ' + entry.name, 'success'); this.openWebdavBrowser(currentPath); } + else { this.terminal.print('Delete failed', 'error'); } + }.bind(this)); + }.bind(this) }); + + items.forEach(function(item) { + var div = document.createElement('div'); + div.textContent = item.label; + div.style.cssText = 'padding:6px 14px;cursor:pointer;font-size:0.85em;'; + div.onmouseover = function() { this.style.background = '#222'; }; + div.onmouseout = function() { this.style.background = ''; }; + div.onclick = function(e) { e.stopPropagation(); menu.remove(); item.fn(); }; + menu.appendChild(div); + }); + + document.body.appendChild(menu); + // Close on click outside + var closeMenu = function(ev) { + if (!menu.contains(ev.target)) { menu.remove(); document.removeEventListener('click', closeMenu); } + }; + setTimeout(function() { document.addEventListener('click', closeMenu); }, 10); } closeWebdavBrowser() { @@ -949,7 +1281,10 @@ class WebDAVModule { async downloadFromWebdav(entry) { try { - var resp = await this._webdavRequest('GET', entry.href); + // Construct path from current browsing path + entry name (avoid autoindex href fragility) + var basePath = (this._browsingPath || '/').replace(/\/+$/, ''); + var fullPath = basePath + '/' + entry.name; + var resp = await this._webdavRequest('GET', fullPath); if (!resp.ok) { this.terminal.print('Download failed: HTTP ' + resp.status, 'error'); return; } var content = await resp.text(); var blob = new Blob([content], { type: 'application/octet-stream' }); @@ -957,7 +1292,7 @@ class WebDAVModule { var a = document.createElement('a'); a.href = url; a.download = entry.name; - a.style.display = 'none'; + a.style.cssText = 'display:none;'; document.body.appendChild(a); a.click(); setTimeout(function() { diff --git a/newos4_vision.html b/newos4_vision.html new file mode 100644 index 0000000..7bdb262 --- /dev/null +++ b/newos4_vision.html @@ -0,0 +1,176 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>newOS4 | The Cognitive OS Vision</title> + <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> + <style> + body { + margin: 0; + padding: 0; + background-color: #000; + color: #00ffcc; + font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; + overflow-x: hidden; + } + #canvas-container { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; + } + .content { + position: relative; + z-index: 1; + max-width: 900px; + margin: 0 auto; + padding: 100px 20px; + text-align: center; + background: rgba(0, 0, 0, 0.7); + backdrop-filter: blur(10px); + box-shadow: 0 0 50px rgba(0, 255, 204, 0.2); + } + h1 { + font-size: 4rem; + text-transform: uppercase; + letter-spacing: 10px; + margin-bottom: 20px; + text-shadow: 0 0 20px #00ffcc; + animation: glow 2s ease-in-out infinite alternate; + } + @keyframes glow { + from { text-shadow: 0 0 10px #00ffcc; } + to { text-shadow: 0 0 30px #00ffcc, 0 0 10px #fff; } + } + .section { + margin: 60px 0; + padding: 30px; + border: 1px solid rgba(0, 255, 204, 0.3); + border-radius: 15px; + background: rgba(0, 20, 20, 0.5); + transition: transform 0.3s ease, box-shadow 0.3s ease; + } + .section:hover { + transform: translateY(-10px) rotateX(5deg); + box-shadow: 0 10px 30px rgba(0, 255, 204, 0.4); + border-color: #00ffcc; + } + h2 { + font-size: 2rem; + color: #fff; + margin-bottom: 20px; + border-bottom: 2px solid #00ffcc; + display: inline-block; + padding-bottom: 10px; + } + p { + font-size: 1.2rem; + line-height: 1.6; + color: #b0fffa; + text-align: left; + } + .roadmap-item { + display: flex; + justify-content: space-between; + margin: 15px 0; + padding: 10px; + background: rgba(255, 255, 255, 0.05); + border-radius: 8px; + } + .phase { + font-weight: bold; + color: #00ffcc; + min-width: 150px; + } + .footer { + margin-top: 100px; + font-size: 0.9rem; + opacity: 0.6; + letter-spacing: 2px; + } + </style> +</head> +<body> + <div id="canvas-container"></div> + + <div class="content"> + <h1>newOS4</h1> + <p style="text-align:center; font-style:italic; margin-bottom:50px;">Architected by Agando | The Evolution of Cognitive Computing</p> + + <div class="section"> + <h2>The Core Philosophy</h2> + <p>newOS4 is a <strong>Cognitive Operating System</strong>. It transcends traditional resource management (CPU/RAM) to manage <strong>Agentic Workflows</strong>. Intelligence is the primary system resource, allowing the OS to reason, adapt, and evolve its own structure in real-time.</p> + </div> + + <div class="section"> + <h2>Architectural Pillars</h2> + <p><strong>Hyper-Modular Kernel:</strong> Real-time self-rewriting drivers for task-specific optimization.</p> + <p><strong>A2A Protocol:</strong> A standardized neural communication layer for seamless agent negotiation.</p> + <p><strong>Neural VFS:</strong> Semantic-based file storage where concepts replace paths.</p> + <p><strong>Recursive Self-Optimization:</strong> Integrated feedback loops that auto-generate WPM patches to eliminate system latency.</p> + </div> + + <div class="section"> + <h2>Strategic Roadmap</h2> + <div class="roadmap-item"> + <span class="phase">Phase 1</span> + <span>Foundation: WebX11 terminal stability, VFS perfection, and Procedural Memory (Skills).</span> + </div> + <div class="roadmap-item"> + <span class="phase">Phase 2 ✅</span> + <span>Agentic Shift: Agent orchestration (list, register, leases, orchestrate). Security layer (security-level 0-3, cmd-allow/deny). Debug panel. WebDAV modern browser.</span> + </div> + <div class="roadmap-item"> + <span class="phase">Phase 3</span> + <span>Cognitive OS: Global World Model, Neural VFS, Recursive self-optimization, Predictive resource allocation.</span> + </div> + </div> + + <div class="footer"> + SYSTEM STATUS: ONLINE | ARCHITECT: AGANDO | VERSION: 4.3.0-BETA + </div> + </div> + + <script> + // Three.js 3D Background - Digital Neural Network + const scene = new THREE.Scene(); + const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); + const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); + renderer.setSize(window.innerWidth, window.innerHeight); + document.getElementById('canvas-container').appendChild(renderer.domElement); + + // Create a point cloud of "nodes" + const geometry = new THREE.BufferGeometry(); + const vertices = []; + for (let i = 0; i < 1000; i++) { + vertices.push(THREE.MathUtils.randFloatSpread(2000)); // x + vertices.push(THREE.MathUtils.randFloatSpread(2000)); // y + vertices.push(THREE.MathUtils.randFloatSpread(2000)); // z + } + geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); + const material = new THREE.PointsMaterial({ color: 0x00ffcc, size: 2 }); + const points = new THREE.Points(geometry, material); + scene.add(points); + + camera.position.z = 500; + + function animate() { + requestAnimationFrame(animate); + points.rotation.x += 0.001; + points.rotation.y += 0.002; + renderer.render(scene, camera); + } + + window.addEventListener('resize', () => { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); + }); + + animate(); + </script> +</body> +</html> diff --git a/newos4_whitepaper.md b/newos4_whitepaper.md new file mode 100644 index 0000000..d8dea0e --- /dev/null +++ b/newos4_whitepaper.md @@ -0,0 +1,234 @@ +# newOS4 — The Cognitive Operating System + +## Whitepaper v4.3.0-beta + +**Author:** Agando +**Status:** Architecture Draft +**Last Updated:** 2026-06-02 + +--- + +## 1. Abstract + +newOS4 is a **Cognitive Operating System** — an agentic, AI-native fabric where intelligence is treated not as an application layer but as the kernel's primary resource. Unlike traditional OSs that manage CPU cycles and memory pages, newOS4 manages **agentic workflows, semantic storage, and recursive self-optimization** as first-class primitives. + +The system is built on four pillars: + +| Pillar | Function | +|---|---| +| **Hyper-Modular Kernel** | Real-time self-rewriting drivers for task-specific optimization | +| **A2A Protocol** | Standardized neural communication layer for agent negotiation | +| **Neural VFS** | Semantic-based file storage where concepts replace paths | +| **Recursive Self-Optimization** | Integrated feedback loops generating WPM patches to eliminate latency | + +--- + +## 2. Motivation & Problem Space + +Modern operating systems (Linux, Windows, macOS) were designed for a world where: + +- Hardware resources (CPU, RAM, disk) are the binding constraints +- Software is statically installed and version-pinned +- The user is the sole intelligent agent at the keyboard + +This model breaks under three converging trends: + +1. **Agent proliferation** — LLM-based agents now outnumber human operators in production workflows +2. **Dynamic compute** — Serverless, edge, and ephemeral compute require OS-level agility +3. **Self-modifying code** — AI systems need to rewrite their own toolchain without reboot cycles + +newOS4 addresses all three by making **intelligence the kernel primitive** rather than an application afterthought. + +--- + +## 3. System Architecture + +### 3.1 Layered Model + +``` +┌─────────────────────────────────────────────────────┐ +│ Cognitive Shell │ +│ (WebX11 Terminal / Agent UI / Graph Dashboard) │ +├─────────────────────────────────────────────────────┤ +│ Agent Orchestration Layer │ +│ (Multi-agent router, sub-agent spawning, leases) │ +├─────────────────────────────────────────────────────┤ +│ A2A Protocol Layer │ +│ (Neural message bus, contract negotiation, RPC) │ +├─────────────────────────────────────────────────────┤ +│ Hyper-Modular Kernel │ +│ (Self-rewriting drivers, syscalls, WPM runtime) │ +├─────────────────────────────────────────────────────┤ +│ Neural VFS + Procedural Memory │ +│ (Semantic paths, skill graphs, fast-context cache) │ +└─────────────────────────────────────────────────────┘ +``` + +### 3.2 The Trinity of Cognition + +newOS4 fuses three cognitive primitives: + +- **Perception** — Sensory input via agent sensors, web fetch, file I/O, and human chat +- **Reasoning** — Contextual inference via LLM backbone (Ollama) + procedural memory (skills) +- **Action** — Tool execution via sandboxed syscalls, WPM patches, and agent delegation + +Each maps to a classic OS concern: Perception = I/O, Reasoning = Scheduler, Action = Execution. + +--- + +## 4. Pillar Deep Dives + +### 4.1 Hyper-Modular Kernel + +The kernel is **not** a monolithic binary. It is a collection of hot-swappable driver modules loaded and versioned through the **WPM (WebX11 Package Manager)**. + +**Properties:** +- Each driver is an isolated ES module with defined syscall surfaces +- Drivers can be loaded/unloaded/replaced at runtime +- A self-rewrite daemon monitors performance telemetry and generates optimized driver variants + +**Warm-start guarantee:** The kernel caches the last N stable driver configurations in VFS. On cold boot, the "golden config" loads first, then adaptive drivers hot-patch in under 200ms. + +### 4.2 A2A Protocol + +Agent-to-Agent (A2A) is a standardized message format and transport layer designed for agent negotiation. See [A2A_protocol_spec.md](A2A_protocol_spec.md) for full specification. + +**Core tenets:** +- No central broker — agents discover each other via VFS registrations +- Messages are typed (request, response, broadcast, lease) +- Each message carries a contract schema defining capabilities and constraints + +### 4.3 Neural VFS + +The Neural Virtual File System replaces hierarchical paths (`/home/user/docs/file.txt`) with semantic addressing (`concept://agentic-workflows/design-patterns/observer`). + +**Key innovation:** Path resolution is a two-stage process: +1. **Semantic index** maps concept tokens to physical blocks +2. **Context cache** keeps recent semantic lookups hot + +See [kernel_api.md](kernel_api.md) for VFS syscall reference. + +### 4.4 Recursive Self-Optimization + +The optimization loop: + +``` +Monitor → Profile → Diagnose → Patch → Test → Promote +``` + +- **Monitor:** Watchdog tracks syscall latency, agent response times, cache hit rates +- **Profile:** Hot-path analysis identifies bottlenecks +- **Diagnose:** LLM analyzes profile data and proposes WPM patch candidates +- **Patch:** Patch is loaded into a sandboxed driver slot +- **Test:** Synthetic workload validates improvement +- **Promote:** On success, patch replaces production driver. On failure, rollback to golden config. + +--- + +## 5. Roadmap + +### Phase 1 — Foundation ✅ COMPLETED + +What the whitepaper originally listed as 4 items has grown into 25+ completed features: + +**Original whitepaper items:** +- ✅ WebX11 terminal — Terminal.js stable with Levenshtein fuzzy suggestions, security tiers, session persistence +- ✅ VFS — virtual-fs.js fully functional: ls, cat, put, rm, upload, download with localStorage backend +- ✅ Procedural Memory — 16 skill files + index.json, module-skills.js +- ✅ Hot-reload module pipeline — module-loader.js + module-manager.js for dynamic load/unload + +**Built beyond the original scope:** +- 32 modules (18 core + 14 ext), cleaned up from 42 +- Proxy API module — replaces dead DuckDuckGo/SearXNG/WebFetcher modules with `/api/search`, `/api/fetch`, `/api/stats` endpoints +- Dynamic Prompt Engine — bare URL auto-fetch+summarize, question auto-search+AI answer +- Ollama Manager — 89 models detected, supports cloud model routing +- AI Tool System — function-calling loop with session_terminal tool, full agent workflow (module-aiassist.js) +- ChatStudio integration — bridges terminal AI to chat UI +- 9 theme presets — dark, light, amber, cyber, ocean, forest, monochrome, sunset, midnight +- Help.html — 70KB, 3-tab layered documentation +- Cheatsheet — printable A4 quick reference +- Fuzzy suggestions — Levenshtein distance for typo correction +- WebDAV browser UI — modern file-tree with bulk actions, inline rename, context menu, keyboard shortcuts +- Voice module — speech and sound support +- BusyBox — shell compatibility layer +- Tutorial + Welcome tour — first-run onboarding +- Ctrl+K quick command cards +- Cryptogzip — encryption/compression utilities +- File manager UI — graphical file browser +- Utilities — calculator, diff, alias, finder, mindmap +- WPM — WebX11 Package Manager (driver/module lifecycle) +- Session nesting fix — recursive session bug resolved +- Debug panel — live memory, events, errors, module health +- Security layer — security-level 0-3 with command allowlist/denylist +- Agent orchestration — agent-list, agent-register, lease-list, orchestrate +- Skill-pack core — 12 skills, auto-load, seed/reload support + +### Phase 2 — Agentic Shift 🟡 IN PROGRESS (partial) + +**What's already built:** +- ✅ AI tool-calling loop with 19+ tool definitions (filesystem, search, skills, module management) +- ✅ session_terminal tool — zero-cost context access for terminal commands +- ✅ Tool execution pipeline with error tracking and auto-skill creation on repeated failures +- ✅ Ollama proxy — routes streaming chat and tool calls with cloud model support +- ✅ Tool failure buffer — auto-flushes to localStorage for cross-session learning +- ✅ Agent orchestration — agent-list, agent-register, lease-list, orchestrate commands +- ✅ Agent registration via VFS — registry with name, model, capabilities metadata +- ✅ Lease system — time-bound contracts with status tracking +- ✅ Security layer — security-level 0-3, cmd-allow/cmd-deny, destructive action confirmation (120s countdown) +- ✅ Debug panel — live memory/events/errors/module health monitoring +- ✅ WebDAV modern browser — file-tree with bulk actions, inline rename, context menu, keyboard shortcuts +- ✅ Debug panel — tool call visualization with persistent log +- ✅ Skill-pack core — 12 skills auto-loaded on startup with seed/reload support +- ✅ Chat cleaned to single /chat toggle overlay + +**What's still to build:** +- Autonomous sub-agent spawning with task-specific roles +- Agent lifecycle management (birth, sleep, death, reanimation) +- A2A Agent-to-Agent protocol — standardized message bus for agent negotiation +- Sandboxed execution contexts for sub-agents +- Agent health monitoring and restart policies +- Neural VFS concept:// path resolution + +### Phase 3 — Cognitive OS 🔴 NOT STARTED + +**On the roadmap:** +- Global World Model integration — persistent semantic understanding of system state +- Predictive resource allocation — anticipate agent needs before they arise +- Cross-session persistent memory graphs — relational memory across all user sessions +- Neural VFS — semantic path resolution (`concept://` paths) replacing hierarchical filesystem +- Recursive self-optimization — automated WPM patches based on performance telemetry +- Self-rewrite daemon — models hot-path bottlenecks and generates optimized driver variants +- Cognitive shell — unified dashboard for multi-agent activity visualization + +**Implementation Plan:** `.hermes/plans/2026-06-02_phase3-cognitive-os.md` + +--- + +## 6. Failure Modes & Risk Mitigation + +| Risk | Mitigation | +|---|---| +| **WPM loop thrash** | N-version golden config, max 3 patches/sec throttle | +| **Cold-start latency** | Warm VFS cache + preloaded golden config | +| **Agent contract deadlock** | Lease TTLs + timeout escalation to human | +| **Semantic path collision** | Namespace registration + conflict resolution via A2A | +| **Self-rewrite corruption** | Boot-time integrity check against signed golden config | + +--- + +## 7. Related Work + +- **Plan 9** — Distributed, per-process namespaces (inspiration for VFS semantics) +- **Erlang/OTP** — Actor model, hot code swapping (inspiration for agent lifecycle) +- **NixOS** — Declarative configuration, rollback (inspiration for golden config model) +- **Auto-GPT / BabyAGI** — Autonomous agent loops (precursors to recursive self-optimization) + +--- + +## 8. Conclusion + +newOS4 is not an application. It is not a framework. It is an operating system designed from first principles for the age of cognitive computing. The architecture is intentionally minimal — four pillars, three phases, one north star: **Intelligence as the OS primitive.** + +``` +STATUS: BETA | ARCHITECT: AGANDO | VERSION: 4.3.0 +```
\ No newline at end of file diff --git a/review-newos4.html b/review-newos4.html index 53e3ffe..4ce41cc 100644 --- a/review-newos4.html +++ b/review-newos4.html @@ -3,7 +3,7 @@ <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <title>Review: newOS4 v4.2 — WebX11 Terminal</title> + <title>Review: newOS4 v4.3 — WebX11 Terminal</title> <link rel="stylesheet" href="/spaces/assets/css/theme.css"> <style> :root { --cs-glass: rgba(17,17,17,0.75); --cs-glass-border: rgba(0,255,136,0.1); } @@ -75,7 +75,7 @@ <body> <header> <div class="header-content"> - <span class="logo"><span class="logo-icon">🧩</span> newOS4 v4.2 Review</span> + <span class="logo"><span class="logo-icon">🧩</span> newOS4 v4.3 Review</span> <div style="display:flex;gap:8px;flex-wrap:wrap;"> <a href="/spaces/newos4/help.html">📖 Help →</a> <a href="/spaces/newos4/main.html">Launch OS →</a> @@ -85,13 +85,13 @@ <div class="container"> -<h1>newOS4 v4.2</h1> -<p class="meta">WebX11 Terminal · In-Browser Terminal OS · Reviewed 2026-06-01</p> +<h1>newOS4 v4.3</h1> +<p class="meta">WebX11 Terminal · In-Browser Terminal OS · Reviewed 2026-06-02</p> <div class="summary-box"> - <div class="stat"><div class="value">42</div><div class="label">Module Files</div></div> - <div class="stat"><div class="value">25+17</div><div class="label">Core + Ext</div></div> - <div class="stat"><div class="value">17</div><div class="label">Skills</div></div> + <div class="stat"><div class="value">32</div><div class="label">Module Files</div></div> + <div class="stat"><div class="value">18+14</div><div class="label">Core + Ext</div></div> + <div class="stat"><div class="value">12</div><div class="label">Skills</div></div> <div class="stat"><div class="value">9</div><div class="label">Theme Presets</div></div> <div class="stat"><div class="value">52KB</div><div class="label">App Size</div></div> <div class="stat"><div class="value">4/5</div><div class="label">Rating</div></div> @@ -99,12 +99,12 @@ <section> <h2>Architecture</h2> - <p>newOS4 v4.2 is an <strong>in-browser terminal OS</strong> — NOT a chat-centric UI. Single-file <code>main.html</code> (1,031 lines, 52KB) loads 42 module files across two directories:</p> + <p>newOS4 v4.3 is an <strong>in-browser terminal OS</strong> — NOT a chat-centric UI. Single-file <code>main.html</code> (1,031 lines, 52KB) loads 32 module files across two directories:</p> <ul> - <li><strong>25 core modules</strong> (<code>modules/core/</code>) — Foundation infrastructure (VirtualFileSystem, OllamaManager, SecretVault, ModuleManager, Terminal) + 19 built-in feature modules (system, fs, modmgr, ollama, my, searxng, servermon, ollamamodels, config, simplegui, io, dynamicprompts, aiassist, aiasm, smartctx, duckduckgo, fetcher, skills, chatstudio)</li> - <li><strong>17 ext modules</strong> (<code>modules/ext/</code>) — Self-registering: alias, busybox, calc, cron, cryptogzip, diff, editor, filemgr, finder, mindmap, shell, skill-pack-core, theme, tutorial, voice, webdav, wpm</li> + <li><strong>18 core modules</strong> (<code>modules/core/</code>) — Foundation infrastructure (VirtualFileSystem, OllamaManager, SecretVault, ModuleManager, Terminal) + 13 built-in feature modules (system, fs, modmgr, ollama, my, searxng, servermon, ollamamodels, config, simplegui, io, dynamicprompts, aiassist, aiasm, smartctx, duckduckgo, fetcher, skills, chatstudio, debug)</li> + <li><strong>14 ext modules</strong> (<code>modules/ext/</code>) — Self-registering: alias, busybox, calc, cron, cryptogzip, diff, editor, filemgr, finder, mindmap, shell, skill-pack-core, theme, tutorial, voice, webdav, wpm</li> </ul> - <p><strong>Total codebase</strong>: ~800KB (main.html + all module files). AI system with 5-turn agentic loop, 19+ tool definitions, 17 skills in VFS, 9 theme presets, and full destructive-action confirmation.</p> + <p><strong>Total codebase</strong>: ~500KB (main.html + all module files). AI system with 5-turn agentic loop, 19+ tool definitions, 12 skills in VFS, 9 theme presets, and full destructive-action confirmation.</p> </section> <section> @@ -141,7 +141,8 @@ <h3>📚 Skills System</h3> <ul> - <li><strong>17 skills</strong> stored as Markdown with YAML frontmatter in VFS <code>/skills/</code></li> + <li><strong>12 skills</strong> stored as Markdown with YAML frontmatter in VFS <code>/skills/</code></li> + <li>Skill-pack core auto-loads skills on startup with seed/reload</li> <li>8 CLI commands: skill-list, skill-view, skill-create, skill-patch, skill-delete, skill-export, skill-import, skill-sync</li> <li>Skills Manager UI modal (📚 button in quick-actions bar)</li> <li>Auto-loads relevant skills at start of every AI task</li> @@ -149,6 +150,29 @@ <li>In-memory index with 2-second debounced flush to VFS</li> </ul> + <h3>🔒 Security Layer</h3> + <ul> + <li><code>security-level 0-3</code> — 0: unrestricted, 1: confirm destructive, 2: deny sensitive, 3: deny all dangerous commands</li> + <li><code>cmd-allow <cmd></code> / <code>cmd-deny <cmd></code> — per-command allowlist/denylist overrides</li> + <li>Destructive action confirmation bar (non-blocking, 120s countdown with color escalation)</li> + <li>AI tool call safety: auto-confirmation for read-only, countdown confirmation for destructive</li> + </ul> + + <h3>🔧 Debug Panel</h3> + <ul> + <li><code>debug</code> toggle — shows memory usage, event listeners, error log, module registration status</li> + <li>Real-time module health: loaded count, broken references, cache state</li> + <li>Useful for troubleshooting module load failures and VFS corruption</li> + </ul> + + <h3>🤖 Agent Orchestration</h3> + <ul> + <li><code>agent-list</code> — list registered AI agents and their status</li> + <li><code>agent-register <name> <model></code> — register a new AI agent</li> + <li><code>lease-list</code> — show active agent leases/tasks</li> + <li><code>orchestrate <task></code> — delegate a multi-step task to the agent orchestrator</li> + </ul> + <h3>🎤 Voice Command</h3> <ul> <li>🎤 button in input area beside Send button</li> @@ -177,7 +201,7 @@ <li><strong>BusyBox</strong> — 16 utilities: which, env, date, cal, yes, seq, base64, printf, cut, uniq, hostname, whoami, basename, dirname, sleep, expand</li> <li><strong>CryptoGzip</strong> — AES-256-GCM encrypt/decrypt + gzip/gunzip for VFS files</li> <li><strong>File Manager</strong> — Fullscreen overlay with 30% sidebar, 70% preview pane, 10+ preview renderers, live search</li> - <li><strong>WebDAV Sync</strong> — 8 commands + modal browser for cloud sync (config, push, pull, backup, restore, del)</li> + <li><strong>WebDAV Sync</strong> — 10 commands + modern file-tree browser with bulk actions (Select All, Delete, Move, Download), inline rename (double-click), right-click context menu, breadcrumb navigation, keyboard shortcuts (j/k, Space, Enter), create-directory button</li> <li><strong>Mindmap</strong> — ASCII mindmap creation and management in terminal</li> <li><strong>Text Editor</strong> — inline VFS editing via modal (edit/save/cancel)</li> <li><strong>Calculator, Cron, Diff, Alias, Finder</strong> — utility modules covering common needs</li> @@ -212,8 +236,12 @@ <section> <h2>Pros & Cons</h2> - <div class="pro"><strong>✅ Self-improving</strong> — AI creates, patches, and manages its own skills. 17 skills in VFS with full CRUD CLI.</div> - <div class="pro"><strong>✅ Rich module ecosystem</strong> — 42 module files (25 core + 17 ext), 19 Unix commands, search, fetch, shell, webdav, cron, calc, voice, encrypt, theme, mindmap, editor</div> + <div class="pro"><strong>✅ Self-improving</strong> — AI creates, patches, and manages its own skills. 12 skills in VFS with skill-pack core auto-load and seed/reload support.</div> + <div class="pro"><strong>✅ Rich module ecosystem</strong> — 32 module files (18 core + 14 ext), 19 Unix commands, search, fetch, shell, webdav, cron, calc, voice, encrypt, theme, mindmap, editor</div> + <div class="pro"><strong>✅ WebDAV modern browser</strong> — File-tree UI with checkboxes, Select All, bulk Move/Delete/Download, inline rename, context menu, breadcrumbs, keyboard shortcuts</div> + <div class="pro"><strong>✅ Security layer</strong> — security-level 0-3, command allowlist/denylist, non-blocking destructive confirmation with 120s countdown</div> + <div class="pro"><strong>✅ Debug panel</strong> — Live memory, event, error, and module health monitoring via <code>debug</code> toggle</div> + <div class="pro"><strong>✅ Agent orchestration</strong> — Agent registration, lease management, multi-step task delegation</div> <div class="pro"><strong>✅ Agentic AI</strong> — 5-turn tool calling loop, 19+ tool definitions, streaming responses, destructive action confirmation with countdown</div> <div class="pro"><strong>✅ 9 Theme Presets</strong> — Dark, light, amber, cyber, ocean, forest, monochrome, sunset, midnight + custom theme creation</div> <div class="pro"><strong>✅ Auto-save sessions</strong> — Dirty-flag auto-save every 60s, VFS crash survival, context restoration on page reload</div> diff --git a/skills/agent-orchestration.md b/skills/agent-orchestration.md new file mode 100644 index 0000000..060ec96 --- /dev/null +++ b/skills/agent-orchestration.md @@ -0,0 +1,35 @@ +--- +name: agent-orchestration +description: How to use the agent registry, leases, and orchestrator for multi-agent workflows +tags: [agents, orchestration, delegation, leases] +category: system +--- +## Steps +1. **Agent Registry:** + - agent-list — list all registered agents (ID, type, capabilities, status) + - agent-register <type> <capabilities> — register a new agent (capabilities: comma-separated like "code,research") + - agent-unregister <id> — remove an agent + - agent-status <id> — detailed status including uptime, task history + +2. **Leases (time-bound contracts):** + - lease-create <agent-id> <ttl-seconds> — create a lease with watchdog timer + - lease-list — show active leases with remaining TTL + - lease-revoke <lease-id> — terminate a lease early + - lease-renew <lease-id> <ttl-seconds> — extend a lease + +3. **Orchestrator (spawn, delegate, kill):** + - orchestrator-spawn <type> <capabilities> — spawn a new agent automatically (registers + creates lease) + - orchestrator-kill <agent-id> — kill an agent (revokes lease + unregisters) + - orchestrate — list all active agents with their orchestrator state + - orchestrator-purge — kill all idle/expired agents + - orchestrator-status — full system status: active, idle, dead agents + +4. **AI tool integration:** + - AI tools list_agents, delegate_task, spawn_agent are available in the AI system prompt + - The AI can auto-spawn worker agents for sub-tasks + - Each agent gets its own VFS workspace at /agents/<agent-id>/ + +5. **When to use:** + - User needs parallel task execution → orchestrator-spawn coder + researcher + - Time-bound automation → lease-create with short TTL + - System cleanup → orchestrator-purge to kill stale agents
\ No newline at end of file diff --git a/skills/bug-tracker.md b/skills/bug-tracker.md new file mode 100644 index 0000000..05963e4 --- /dev/null +++ b/skills/bug-tracker.md @@ -0,0 +1,30 @@ +--- +name: bug-tracker +description: A systematic approach to tracking and managing bugs in the VFS. +tags: [dev, debugging, management, workflow] +--- + +# Bug Tracking Workflow + +This skill provides a standardized way to track bugs using a JSON-based log file in the root directory. + +## 1. Initialize Bug Log +If `/bugs.json` does not exist, create it with an empty array: `[]`. + +## 2. Reporting a Bug +When a bug is found, append a new entry to `/bugs.json` with the following schema: +- `id`: Unique incremental integer. +- `title`: Brief summary of the issue. +- `description`: Detailed steps to reproduce and expected vs actual behavior. +- `status`: "open" | "in-progress" | "resolved" | "closed". +- `severity`: "low" | "medium" | "high" | "critical". +- `created_at`: Timestamp. +- `updated_at`: Timestamp. + +## 3. Managing Bugs +- **List Open Bugs**: Filter `/bugs.json` for entries where `status != "closed"`. +- **Update Status**: Change the `status` and update the `updated_at` timestamp. +- **Resolve**: Mark as "resolved" and add a `resolution` field explaining the fix. + +## 4. Verification +Once a bug is marked "resolved", run existing tests or manually verify the fix before marking it "closed". diff --git a/skills/debug-panel-and-logs.md b/skills/debug-panel-and-logs.md new file mode 100644 index 0000000..b182e23 --- /dev/null +++ b/skills/debug-panel-and-logs.md @@ -0,0 +1,34 @@ +--- +name: debug-panel-and-logs +description: How to use the debug panel, session logging, and terminal diagnostics +tags: [debug, logging, diagnostics, troubleshooting] +category: system +--- +## Steps +1. **Toggle debug panel:** + - debug — opens real-time debug panel showing last AI tool call, response time, and context + - Debug panel includes Copy, Export, and Write to VFS buttons + - Export: saves tool call log as timestamped JSON to downloads + - Write to VFS: appends debug data to /debug.log (dedup by turn ID) + +2. **Session logging:** + - logs [n] [LEVEL] — view last n log entries (optionally filtered by INFO/WARN/ERROR) + - logrotate — rotate current.log to dated archive (auto-cleans old logs beyond 10) + - Logs stored in /logs/ directory: current.log + dated archives + - Health check runs automatically after complex tasks + +3. **Diagnostic commands:** + - health-check — full audit: VFS size, modules, logs, trash, skills + - check — quick system health including module requirement status + - mem — real-time memory usage (also in header) + - status — network, files, modules, Ollama connectivity + +4. **System journals:** + - debug.log — appended by debug panel VFS button (dedup by turn ID) + - current.log — system activity log with rotation + - Auto-save: terminal state auto-saves every 60s + +5. **When to use:** + - AI is behaving unexpectedly → debug to see tool calls + - User reports errors → logs 50 ERROR to see error entries + - Memory concerns → mem to check, health-check for full audit
\ No newline at end of file diff --git a/skills/index.json b/skills/index.json index 6d57936..a1254ae 100644 --- a/skills/index.json +++ b/skills/index.json @@ -1 +1,248 @@ -{"navigating-the-terminal":{"name":"navigating-the-terminal","description":"How to explore the WebX11 terminal, find commands, check status, and navigate the filesystem","tags":["terminal","navigation","help","filesystem"],"category":"system","created":"2026-05-28T15:39:51.676Z","updated":"2026-05-28T15:39:51.676Z"},"memory-and-profile":{"name":"memory-and-profile","description":"How to use the user profile system, session memory, and pattern tracking for persistent context","tags":["memory","profile","session","persistence"],"category":"system","created":"2026-05-28T15:39:51.676Z","updated":"2026-05-28T15:39:51.676Z"},"web-search-and-fetch":{"name":"web-search-and-fetch","description":"How to search the web and fetch URL content using the WebX11 proxy API","tags":["search","fetch","web","proxy","api"],"category":"research","created":"2026-05-28T15:39:51.676Z","updated":"2026-05-28T15:39:51.676Z"},"module-management":{"name":"module-management","description":"How to view, edit, test, reload modules and manage startup configuration","tags":["modules","editing","code","self-improvement"],"category":"dev","created":"2026-05-28T15:39:51.676Z","updated":"2026-05-28T15:39:51.676Z"},"system-maintenance":{"name":"system-maintenance","description":"How to reset, troubleshoot, and maintain the WebX11 terminal","tags":["system","maintenance","reset","troubleshoot","config"],"category":"system","created":"2026-05-28T15:39:51.676Z","updated":"2026-05-28T15:39:51.676Z"},"backup-and-restore":{"name":"backup-and-restore","description":"How to backup, restore, and export WebX11 data","tags":["backup","restore","export","import","persistence"],"category":"system","created":"2026-05-28T15:39:51.676Z","updated":"2026-05-28T15:39:51.676Z"},"mindmap-workflow":{"name":"mindmap-workflow","description":"How to create, view, edit, and manage brainstorming mindmaps","tags":["mindmap","brainstorming","planning","organization"],"category":"productivity","created":"2026-05-28T15:39:51.677Z","updated":"2026-05-28T15:39:51.677Z"},"skills-system-guide":{"name":"skills-system-guide","description":"How to use the skills system: load, create, patch, list, and audit skills for persistent procedural memory","tags":["skills","system","meta"],"category":"system","created":"2026-05-28T15:39:51.677Z","updated":"2026-05-28T15:39:51.677Z"},"mindmap-user-guide":{"name":"mindmap-user-guide","description":"How to create, view, and manage brainstorming mindmaps using the mindmap terminal module","tags":["mindmap","brainstorming","organization","planning"],"category":"productivity","created":"2026-05-28T15:40:02.250Z","updated":"2026-05-28T15:40:02.250Z"},"webx11-task-manager":{"name":"webx11-task-manager","description":"","tags":["task-management","productivity","vfs","organization"],"category":"system","created":"2026-05-28T16:32:01.429Z","updated":"2026-05-28T16:32:01.430Z"},"mindmap-architect-3d":{"name":"mindmap-architect-3d","description":"","tags":["visual","map","3d","mermaid","ui"],"category":"dev","created":"2026-05-29T00:10:49.903Z","updated":"2026-05-29T00:10:49.903Z"},"mindmap-navigation-pro":{"name":"mindmap-navigation-pro","description":"","tags":["ui","mobile","js","mindmap"],"category":"ui-dev","created":"2026-05-29T00:22:20.790Z","updated":"2026-05-29T00:22:20.790Z"},"agent-optimization-framework":{"name":"agent-optimization-framework","description":"","tags":["optimization","agentic-workflow","efficiency","tool-calls"],"category":"system","created":"2026-05-29T14:52:23.451Z","updated":"2026-05-29T14:52:23.451Z"},"macro-engine-debug":{"name":"macro-engine-debug","description":"","tags":["debug","a-repair","macro"],"category":"system","created":"2026-05-29T15:06:24.057Z","updated":"2026-05-29T15:06:24.057Z"},"macro-engine-onboarding":{"name":"macro-engine-onboarding","description":"","tags":["onboarding","context","macro"],"category":"system","created":"2026-05-29T15:06:24.064Z","updated":"2026-05-29T15:06:24.064Z"},"project-profile-manager":{"name":"project-profile-manager","description":"","tags":["context","memory","workspace"],"category":"system","created":"2026-05-29T15:08:44.897Z","updated":"2026-05-29T15:08:44.897Z"}}
\ No newline at end of file +{ + "navigating-the-terminal": { + "name": "navigating-the-terminal", + "description": "How to explore the WebX11 terminal, find commands, check status, and navigate the filesystem", + "tags": [ + "terminal", + "navigation", + "help", + "filesystem" + ], + "category": "system", + "created": "2026-06-02T13:19:44.542Z", + "updated": "2026-06-02T13:19:44.542Z" + }, + "memory-and-profile": { + "name": "memory-and-profile", + "description": "How to use the user profile system, session memory, and pattern tracking for persistent context", + "tags": [ + "memory", + "profile", + "session", + "persistence" + ], + "category": "system", + "created": "2026-06-02T13:19:44.542Z", + "updated": "2026-06-02T13:19:44.542Z" + }, + "web-search-and-fetch": { + "name": "web-search-and-fetch", + "description": "How to search the web and fetch URL content using the WebX11 proxy API", + "tags": [ + "search", + "fetch", + "web", + "proxy", + "api" + ], + "category": "research", + "created": "2026-06-02T13:19:44.542Z", + "updated": "2026-06-02T13:19:44.542Z" + }, + "module-management": { + "name": "module-management", + "description": "How to view, edit, test, reload modules and manage startup configuration", + "tags": [ + "modules", + "editing", + "code", + "self-improvement" + ], + "category": "dev", + "created": "2026-06-02T13:19:44.542Z", + "updated": "2026-06-02T13:19:44.542Z" + }, + "system-maintenance": { + "name": "system-maintenance", + "description": "How to reset, troubleshoot, and maintain the WebX11 terminal", + "tags": [ + "system", + "maintenance", + "reset", + "troubleshoot", + "config" + ], + "category": "system", + "created": "2026-06-02T13:19:44.543Z", + "updated": "2026-06-02T13:19:44.543Z" + }, + "backup-and-restore": { + "name": "backup-and-restore", + "description": "How to backup, restore, and export WebX11 data", + "tags": [ + "backup", + "restore", + "export", + "import", + "persistence" + ], + "category": "system", + "created": "2026-06-02T13:19:44.543Z", + "updated": "2026-06-02T13:19:44.543Z" + }, + "mindmap-workflow": { + "name": "mindmap-workflow", + "description": "How to create, view, edit, and manage brainstorming mindmaps", + "tags": [ + "mindmap", + "brainstorming", + "planning", + "organization" + ], + "category": "productivity", + "created": "2026-06-02T13:19:44.543Z", + "updated": "2026-06-02T13:19:44.543Z" + }, + "skills-system-guide": { + "name": "skills-system-guide", + "description": "How to use the skills system: load, create, patch, list, and audit skills for persistent procedural memory", + "tags": [ + "skills", + "system", + "meta" + ], + "category": "system", + "created": "2026-06-02T13:19:44.543Z", + "updated": "2026-06-02T13:19:44.543Z" + }, + "agent-orchestration": { + "name": "agent-orchestration", + "description": "How to use the agent registry, leases, and orchestrator for multi-agent workflows", + "tags": [ + "agents", + "orchestration", + "delegation", + "leases" + ], + "category": "system", + "created": "2026-06-02T13:19:44.543Z", + "updated": "2026-06-02T13:19:44.543Z" + }, + "debug-panel-and-logs": { + "name": "debug-panel-and-logs", + "description": "How to use the debug panel, session logging, and terminal diagnostics", + "tags": [ + "debug", + "logging", + "diagnostics", + "troubleshooting" + ], + "category": "system", + "created": "2026-06-02T13:19:44.543Z", + "updated": "2026-06-02T13:19:44.543Z" + }, + "webdav-operations": { + "name": "webdav-operations", + "description": "How to use WebDAV cloud storage for backup, file sync, and remote file access", + "tags": [ + "webdav", + "cloud", + "backup", + "sync", + "storage" + ], + "category": "system", + "created": "2026-06-02T13:19:44.543Z", + "updated": "2026-06-02T13:19:44.543Z" + }, + "security-and-safety": { + "name": "security-and-safety", + "description": "How to use WebX11 security levels, command blocking, and safety features", + "tags": [ + "security", + "safety", + "blocking", + "permissions" + ], + "category": "system", + "created": "2026-06-02T13:19:44.543Z", + "updated": "2026-06-02T13:19:44.543Z" + }, + "mindmap-user-guide": { + "name": "mindmap-user-guide", + "description": "How to create, view, and manage brainstorming mindmaps using the mindmap terminal module", + "tags": [ + "mindmap", + "brainstorming", + "organization", + "planning" + ], + "category": "productivity", + "created": "2026-06-02T13:20:28.672Z", + "updated": "2026-06-02T13:20:28.672Z" + }, + "bug-tracker": { + "name": "bug-tracker", + "description": "Systematic workflow for tracking bugs using a JSON log file in the VFS.", + "tags": [ + "dev", + "debugging", + "management", + "workflow" + ], + "category": "dev", + "created": "2026-06-02T10:10:44.447Z", + "updated": "2026-06-02T10:10:44.447Z" + }, + "task-manager": { + "name": "task-manager", + "description": "A systematic workflow for tracking and managing tasks via a VFS JSON store.", + "tags": [ + "productivity", + "tasks", + "vfs" + ], + "category": "productivity", + "created": "2026-06-02T10:12:44.476Z", + "updated": "2026-06-02T10:12:44.476Z" + }, + "agent-optimization-framework": { + "name": "agent-optimization-framework", + "description": "", + "tags": [ + "optimization", + "agentic-workflow", + "efficiency", + "tool-calls" + ], + "category": "system", + "created": "2026-05-29T14:52:23.451Z", + "updated": "2026-05-29T14:52:23.451Z" + }, + "macro-engine-debug": { + "name": "macro-engine-debug", + "description": "", + "tags": [ + "debug", + "a-repair", + "macro" + ], + "category": "system", + "created": "2026-05-29T15:06:24.057Z", + "updated": "2026-05-29T15:06:24.057Z" + }, + "macro-engine-onboarding": { + "name": "macro-engine-onboarding", + "description": "", + "tags": [ + "onboarding", + "context", + "macro" + ], + "category": "system", + "created": "2026-05-29T15:06:24.064Z", + "updated": "2026-05-29T15:06:24.064Z" + }, + "project-profile-manager": { + "name": "project-profile-manager", + "description": "", + "tags": [ + "context", + "memory", + "workspace" + ], + "category": "system", + "created": "2026-05-29T15:08:44.897Z", + "updated": "2026-05-29T15:08:44.897Z" + } +}
\ No newline at end of file diff --git a/skills/security-and-safety.md b/skills/security-and-safety.md new file mode 100644 index 0000000..bd4f23b --- /dev/null +++ b/skills/security-and-safety.md @@ -0,0 +1,37 @@ +--- +name: security-and-safety +description: How to use WebX11 security levels, command blocking, and safety features +tags: [security, safety, blocking, permissions] +category: system +--- +## Steps +1. **Security levels:** + - security-level <0-3> — set security level: + 0 = Permissive (no blocks, no confirms — anything runs) + 1 = Standard (unknown cmds → AI route, destructive → confirm, system → block) + 2 = Paranoid (unknown cmds blocked entirely, destructive blocked) + 3 = Audit (everything allowed, everything logged) + - Default: level 1 + - security — show current level, blocked count, and stats + +2. **Command control:** + - cmd-allow <cmd> — whitelist a command from security checks + - cmd-deny <cmd> — blacklist/block a command entirely + - whitelist built-in: help, clear, ls, cat, pwd, whoami, df, free, top, ps, echo, date, env, history + +3. **What gets blocked at each level:** + - Unknown commands: Level 1 → AI routed, Level 2+ → blocked + - Destructive commands (delete, write, save, format, etc.): Level 1 → confirmation, Level 2+ → blocked + - System commands (reboot, shutdown, format, kill): Level 1+ → blocked + - Suspicious flags (--execute, --eval, --shell): Level 1+ → blocked + +4. **Safety checklist:** + - Check security: security shows your current level + - Before running risky commands: create a checkpoint first + - For temporary relax: security-level 0, then restore to 1 + - For total lockdown before leaving: security-level 2 + +5. **When to use:** + - User wants to lock down the terminal → security-level 2 + - User wants to allow a blocked command → cmd-allow <cmd> + - Debugging strange behavior → verify security-level (user may have set it higher)
\ No newline at end of file diff --git a/skills/task-manager.md b/skills/task-manager.md new file mode 100644 index 0000000..74c3da1 --- /dev/null +++ b/skills/task-manager.md @@ -0,0 +1,37 @@ +--- +name: task-manager +description: A systematic approach to managing tasks using a JSON-based tracking system in the VFS. +tags: productivity, management, organization +--- + +# Task Management Workflow + +## 1. Initialization +- Check if `/tasks.json` exists. If not, create it with an empty array: `[]`. + +## 2. Adding Tasks +When a new task is identified: +- **Define**: Task name, priority (Low, Medium, High), status (Todo, In-Progress, Done), and deadline. +- **Action**: Read `tasks.json`, append the new task object, and write back to the file. + +## 3. Daily Planning/Review +- **List**: Read `tasks.json` and filter for "Todo" or "In-Progress". +- **Prioritize**: Sort tasks by priority (High > Medium > Low). +- **Update**: Change status of tasks as they are completed. + +## 4. Maintenance +- **Cleanup**: Archive "Done" tasks to `/tasks_archive.json` periodically to keep the main list lean. +- **Audit**: Review overdue tasks and update deadlines or priorities. + +## Schema Reference +```json +{ + "id": "unique-id", + "title": "Task name", + "priority": "High|Medium|Low", + "status": "Todo|In-Progress|Done", + "created_at": "ISO-Date", + "deadline": "ISO-Date", + "notes": "Additional context" +} +```
\ No newline at end of file diff --git a/skills/webdav-operations.md b/skills/webdav-operations.md new file mode 100644 index 0000000..098449c --- /dev/null +++ b/skills/webdav-operations.md @@ -0,0 +1,33 @@ +--- +name: webdav-operations +description: How to use WebDAV cloud storage for backup, file sync, and remote file access +tags: [webdav, cloud, backup, sync, storage] +category: system +--- +## Steps +1. **Configuration:** + - webdav-config — configure WebDAV endpoint URL and credentials + - Connection auto-tested on config; webdav-status shows connection health + - Uses per-user token stored in .token file on the server side + +2. **File operations:** + - webdav-ls [path] — list remote directory contents + - webdav-push <local-path> <remote-path> — upload file to WebDAV + - webdav-pull <remote-path> [local-path] — download file from WebDAV + - webdav-explorer — GUI file browser for WebDAV + - webdav-browser — CLI file browser for WebDAV + +3. **Backup & Restore:** + - webdav-backup — push full VFS snapshot to WebDAV (creates /backups/newos4-<date>.json) + - webdav-backup-list — list all remote backup snapshots + - webdav-restore <filename> — restore VFS from a remote backup snapshot + +4. **Status & Diagnostics:** + - webdav-status — connection status, last sync, space information + - If uploads fail: check webdav-config, ensure .token file is valid + - WebDAV runs on the same server (nginx with WebDAV module), no external dependency + +5. **When to use:** + - Before factory-reset: webdav-backup to store VFS remotely + - Cross-browser: webdav-push/pull to move files between instances + - User says "cloud backup" or "remote save" → webdav-backup
\ No newline at end of file |