aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-10-23 15:28:57 +0200
committerAgando <roduccomm@gmail.com>2026-10-23 15:28:57 +0200
commit2aa908775e2660223115f62dde83549370d4cd84 (patch)
treeeb55ed7e4f708f2086b27003d12d654dcb30b7ef
parent7c48b16cb59a514217e275b0a32cece3c74d2a24 (diff)
fix(newos4): define VirtualFS.emptyTrash() — empty-trash threw since 2026-10-18
Report RPT-20261018-164646-89352963 ("this.fs.emptyTrash is not a function") was filed by the auto-filer and never fixed: terminal.js:1777 called a method VirtualFS never defined, so 'empty-trash --confirm' threw instead of emptying. Added emptyTrash() returning true only when it removed something. Verified live on v4.6.54: put -> rm -> empty-trash --confirm => 'Trash emptied.'; second run => 'Trash is already empty.'; .trash entries 0.
-rw-r--r--downloads/bugs/BUG-REPORT.html302
-rw-r--r--main.html6
-rw-r--r--modules/core/core-bundle.js13
-rw-r--r--modules/core/module-loader.js2
-rw-r--r--modules/core/virtual-fs.js27
5 files changed, 343 insertions, 7 deletions
diff --git a/downloads/bugs/BUG-REPORT.html b/downloads/bugs/BUG-REPORT.html
index 072984d..9b6e7be 100644
--- a/downloads/bugs/BUG-REPORT.html
+++ b/downloads/bugs/BUG-REPORT.html
@@ -35,10 +35,306 @@
<h1>🐞 newOS4 Bug Reports</h1>
<p class="sub">Generated from <code>data/reports/*.json</code> — no manual edits.</p>
<section class="stats">
- <div class="stat"><b>0</b><span>reports</span></div>
- <div class="stat"><b>—</b><span>latest report</span></div>
+ <div class="stat"><b>10</b><span>reports</span></div>
+ <div class="stat"><b>2026-10-23T12:41:01+00:00</b><span>latest report</span></div>
</section>
- <p class="empty">No reports yet. Run <code>bug &lt;title&gt;</code> in newOS4.</p>
+ <article class="card">
+ <header>
+ <h3>Auto: PROBE 2026-10-23 delta-sync verification (safe to ignore)</h3>
+ <span class="pill">RPT-20261023-124101-6151bb46</span>
+ </header>
+ <p class="meta">2026-10-23T12:41:01+00:00 · version v4.6.53 · 1 error(s)</p>
+ <p class="body">Automatically filed by the debug supervisor (no user action).</p>
+ <details>
+ <summary>Diagnostics</summary>
+ <pre>{
+ &quot;version&quot;: &quot;v4.6.53&quot;,
+ &quot;url&quot;: &quot;https://webx11.duckdns.org/spaces/newos4/main.html&quot;,
+ &quot;ua&quot;: &quot;Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/155.0.0.0 Safari/537.36&quot;,
+ &quot;auto&quot;: true,
+ &quot;errors&quot;: [
+ {
+ &quot;message&quot;: &quot;PROBE 2026-10-23 delta-sync verification (safe to ignore)&quot;,
+ &quot;source&quot;: &quot;probe-delta&quot;,
+ &quot;type&quot;: &quot;runtime&quot;,
+ &quot;severity&quot;: &quot;error&quot;,
+ &quot;count&quot;: 1
+ }
+ ]
+}</pre>
+ </details>
+ </article>
+ <article class="card">
+ <header>
+ <h3>Auto: Fetch failed: https://webx11.duckdns.org/ollama//api/chat — The user aborted a request.</h3>
+ <span class="pill">RPT-20261021-195519-96430378</span>
+ </header>
+ <p class="meta">2026-10-21T19:55:19+00:00 · version v4.6.44 · 1 error(s)</p>
+ <p class="body">Automatically filed by the debug supervisor (no user action).</p>
+ <details>
+ <summary>Diagnostics</summary>
+ <pre>{
+ &quot;version&quot;: &quot;v4.6.44&quot;,
+ &quot;url&quot;: &quot;https://webx11.duckdns.org/spaces/newos4/main.html&quot;,
+ &quot;ua&quot;: &quot;Mozilla/5.0 (Linux; Android 12; SM-A125F Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.88 Mobile Safari/537.36&quot;,
+ &quot;auto&quot;: true,
+ &quot;errors&quot;: [
+ {
+ &quot;message&quot;: &quot;Fetch failed: https://webx11.duckdns.org/ollama//api/chat — The user aborted a request.&quot;,
+ &quot;source&quot;: &quot;https://webx11.duckdns.org/ollama//api/chat&quot;,
+ &quot;type&quot;: &quot;runtime&quot;,
+ &quot;severity&quot;: &quot;error&quot;,
+ &quot;count&quot;: 1
+ }
+ ]
+}</pre>
+ </details>
+ </article>
+ <article class="card">
+ <header>
+ <h3>Auto: FAIL: file too small: 689</h3>
+ <span class="pill">RPT-20261021-164409-5ca01408</span>
+ </header>
+ <p class="meta">2026-10-21T16:44:09+00:00 · version v4.6.44 · 1 error(s)</p>
+ <p class="body">Automatically filed by the debug supervisor (no user action).</p>
+ <details>
+ <summary>Diagnostics</summary>
+ <pre>{
+ &quot;version&quot;: &quot;v4.6.44&quot;,
+ &quot;url&quot;: &quot;https://webx11.duckdns.org/spaces/newos4/main.html&quot;,
+ &quot;ua&quot;: &quot;Mozilla/5.0 (Linux; Android 12; SM-A125F Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.88 Mobile Safari/537.36&quot;,
+ &quot;auto&quot;: true,
+ &quot;errors&quot;: [
+ {
+ &quot;message&quot;: &quot;FAIL: file too small: 689&quot;,
+ &quot;source&quot;: &quot;console.error&quot;,
+ &quot;type&quot;: &quot;runtime&quot;,
+ &quot;severity&quot;: &quot;error&quot;,
+ &quot;count&quot;: 1
+ }
+ ]
+}</pre>
+ </details>
+ </article>
+ <article class="card">
+ <header>
+ <h3>Auto: Fetch failed: https://webx11.duckdns.org/ollama//api/chat — Failed to fetch</h3>
+ <span class="pill">RPT-20261020-190349-c6c64512</span>
+ </header>
+ <p class="meta">2026-10-20T19:03:49+00:00 · version v4.6.44 · 1 error(s)</p>
+ <p class="body">Automatically filed by the debug supervisor (no user action).</p>
+ <details>
+ <summary>Diagnostics</summary>
+ <pre>{
+ &quot;version&quot;: &quot;v4.6.44&quot;,
+ &quot;url&quot;: &quot;https://webx11.duckdns.org/spaces/newos4/main.html&quot;,
+ &quot;ua&quot;: &quot;Mozilla/5.0 (Linux; Android 12; SM-A125F Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.88 Mobile Safari/537.36&quot;,
+ &quot;auto&quot;: true,
+ &quot;errors&quot;: [
+ {
+ &quot;message&quot;: &quot;Fetch failed: https://webx11.duckdns.org/ollama//api/chat — Failed to fetch&quot;,
+ &quot;source&quot;: &quot;https://webx11.duckdns.org/ollama//api/chat&quot;,
+ &quot;type&quot;: &quot;HTTP&quot;,
+ &quot;severity&quot;: &quot;error&quot;,
+ &quot;count&quot;: 1
+ }
+ ]
+}</pre>
+ </details>
+ </article>
+ <article class="card">
+ <header>
+ <h3>Auto: Cannot read properties of undefined (reading &#x27;toLowerCase&#x27;)</h3>
+ <span class="pill">RPT-20261020-165652-abf26e3a</span>
+ </header>
+ <p class="meta">2026-10-20T16:56:52+00:00 · version v4.6.44 · 1 error(s)</p>
+ <p class="body">Automatically filed by the debug supervisor (no user action).</p>
+ <details>
+ <summary>Diagnostics</summary>
+ <pre>{
+ &quot;version&quot;: &quot;v4.6.44&quot;,
+ &quot;url&quot;: &quot;https://webx11.duckdns.org/spaces/newos4/main.html&quot;,
+ &quot;ua&quot;: &quot;Mozilla/5.0 (X11; Ubuntu; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36&quot;,
+ &quot;auto&quot;: true,
+ &quot;errors&quot;: [
+ {
+ &quot;message&quot;: &quot;Cannot read properties of undefined (reading &#x27;toLowerCase&#x27;)&quot;,
+ &quot;source&quot;: &quot;unhandledrejection&quot;,
+ &quot;type&quot;: &quot;runtime&quot;,
+ &quot;severity&quot;: &quot;error&quot;,
+ &quot;count&quot;: 1
+ }
+ ]
+}</pre>
+ </details>
+ </article>
+ <article class="card">
+ <header>
+ <h3>Auto: Uncaught SyntaxError: Invalid or unexpected token — Invalid or unexpected token (https://webx11.duckdns.org/spaces/newos</h3>
+ <span class="pill">RPT-20261020-144427-c01ee147</span>
+ </header>
+ <p class="meta">2026-10-20T14:44:27+00:00 · version v4.6.44 · 1 error(s)</p>
+ <p class="body">Automatically filed by the debug supervisor (no user action).</p>
+ <details>
+ <summary>Diagnostics</summary>
+ <pre>{
+ &quot;version&quot;: &quot;v4.6.44&quot;,
+ &quot;url&quot;: &quot;https://webx11.duckdns.org/spaces/newos4/main.html&quot;,
+ &quot;ua&quot;: &quot;Mozilla/5.0 (X11; Ubuntu; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36&quot;,
+ &quot;auto&quot;: true,
+ &quot;errors&quot;: [
+ {
+ &quot;message&quot;: &quot;Uncaught SyntaxError: Invalid or unexpected token — Invalid or unexpected token (https://webx11.duckdns.org/spaces/newos4/main.html:1:19)&quot;,
+ &quot;source&quot;: &quot;https://webx11.duckdns.org/spaces/newos4/main.html&quot;,
+ &quot;type&quot;: &quot;SyntaxError&quot;,
+ &quot;severity&quot;: &quot;fatal&quot;,
+ &quot;count&quot;: 1
+ }
+ ]
+}</pre>
+ </details>
+ </article>
+ <article class="card">
+ <header>
+ <h3>Auto: Fetch failed: https://webx11.duckdns.org/ollama//api/chat — The user aborted a request.</h3>
+ <span class="pill">RPT-20261018-165522-ac5318c4</span>
+ </header>
+ <p class="meta">2026-10-18T16:55:22+00:00 · version v4.6.44 · 1 error(s)</p>
+ <p class="body">Automatically filed by the debug supervisor (no user action).</p>
+ <details>
+ <summary>Diagnostics</summary>
+ <pre>{
+ &quot;version&quot;: &quot;v4.6.44&quot;,
+ &quot;url&quot;: &quot;https://webx11.duckdns.org/spaces/newos4/main.html&quot;,
+ &quot;ua&quot;: &quot;Mozilla/5.0 (Linux; Android 12; SM-A125F Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.88 Mobile Safari/537.36&quot;,
+ &quot;auto&quot;: true,
+ &quot;errors&quot;: [
+ {
+ &quot;message&quot;: &quot;Fetch failed: https://webx11.duckdns.org/ollama//api/chat — The user aborted a request.&quot;,
+ &quot;source&quot;: &quot;https://webx11.duckdns.org/ollama//api/chat&quot;,
+ &quot;type&quot;: &quot;runtime&quot;,
+ &quot;severity&quot;: &quot;error&quot;,
+ &quot;count&quot;: 1
+ }
+ ]
+}</pre>
+ </details>
+ </article>
+ <article class="card">
+ <header>
+ <h3>Auto: this.fs.emptyTrash is not a function</h3>
+ <span class="pill">RPT-20261018-164646-89352963</span>
+ </header>
+ <p class="meta">2026-10-18T16:46:46+00:00 · version v4.6.42 · 1 error(s)</p>
+ <p class="body">Automatically filed by the debug supervisor (no user action).</p>
+ <details>
+ <summary>Diagnostics</summary>
+ <pre>{
+ &quot;version&quot;: &quot;v4.6.42&quot;,
+ &quot;url&quot;: &quot;https://webx11.duckdns.org/spaces/newos4/main.html&quot;,
+ &quot;ua&quot;: &quot;Mozilla/5.0 (Linux; Android 12; SM-A125F Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.88 Mobile Safari/537.36&quot;,
+ &quot;auto&quot;: true,
+ &quot;errors&quot;: [
+ {
+ &quot;message&quot;: &quot;this.fs.emptyTrash is not a function&quot;,
+ &quot;source&quot;: &quot;unhandledrejection&quot;,
+ &quot;type&quot;: &quot;TypeError&quot;,
+ &quot;severity&quot;: &quot;error&quot;,
+ &quot;count&quot;: 1
+ }
+ ]
+}</pre>
+ </details>
+ </article>
+ <article class="card">
+ <header>
+ <h3>Untitled bug report</h3>
+ <span class="pill">RPT-20261018-163934-165c02e0</span>
+ </header>
+ <p class="meta">2026-10-18T16:39:34+00:00 · version v4.6.42 · 7 error(s)</p>
+
+ <details>
+ <summary>Diagnostics</summary>
+ <pre>{
+ &quot;version&quot;: &quot;v4.6.42&quot;,
+ &quot;url&quot;: &quot;https://webx11.duckdns.org/spaces/newos4/main.html&quot;,
+ &quot;ua&quot;: &quot;Mozilla/5.0 (Linux; Android 12; SM-A125F Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.88 Mobile Safari/537.36&quot;,
+ &quot;online&quot;: true,
+ &quot;modules&quot;: 46,
+ &quot;errors&quot;: [
+ {
+ &quot;message&quot;: &quot;Fetch failed: https://webx11.duckdns.org/ollama//api/chat — Failed to fetch&quot;,
+ &quot;source&quot;: &quot;https://webx11.duckdns.org/ollama//api/chat&quot;,
+ &quot;count&quot;: 9
+ },
+ {
+ &quot;message&quot;: &quot;Cannot read properties of undefined (reading &#x27;toLowerCase&#x27;)&quot;,
+ &quot;source&quot;: &quot;unhandledrejection&quot;,
+ &quot;count&quot;: 1
+ },
+ {
+ &quot;message&quot;: &quot;Fetch failed: https://webx11.duckdns.org/ollama//api/chat — The user aborted a request.&quot;,
+ &quot;source&quot;: &quot;https://webx11.duckdns.org/ollama//api/chat&quot;,
+ &quot;count&quot;: 5
+ },
+ {
+ &quot;message&quot;: &quot;Uncaught SyntaxError: Invalid or unexpected token — Invalid or unexpected token (https://webx11.duckdns.org/spaces/newos4/main.html:1:19)&quot;,
+ &quot;source&quot;: &quot;https://webx11.duckdns.org/spaces/newos4/main.html&quot;,
+ &quot;count&quot;: 1
+ },
+ {
+ &quot;message&quot;: &quot;ASYNCOK is not defined&quot;,
+ &quot;source&quot;: &quot;unhandledrejection&quot;,
+ &quot;count&quot;: 1
+ },
+ {
+ &quot;message&quot;: &quot;[WorldModel] Save failed: temp file corrupt after write&quot;,
+ &quot;source&quot;: &quot;console.error&quot;,
+ &quot;count&quot;: 39
+ },
+ {
+ &quot;message&quot;: &quot;Cannot read properties of null (reading &#x27;addEventListener&#x27;)&quot;,
+ &quot;source&quot;: &quot;unhandledrejection&quot;,
+ &quot;count&quot;: 2
+ }
+ ],
+ &quot;recent_commands&quot;: [
+ &quot;this erro Security: AI error: tc is not defined&quot;,
+ &quot;somethimg has broken&quot;,
+ &quot;session-new&quot;,
+ &quot;ls agents/&quot;,
+ &quot;✅ tc is not defined — found, fixed, verified\n\nSame bug family as the /agents/ leak: a refactor moved code, an identifier didn&#x27;t move with it, and a broad catch buried the evidence.\n\nRoot cause — module-aiassist.js, chat() tool executor\n\nThe \&quot;execute non-destructive tools sequentially\&quot; refactor split one loop into two passes:\n\nfor (const tc of safeTools) {\n ...\n safeResults.push({ fn, toolResult, t1 }); // :1576 ← tc not carried over\n}\n\nfor (const { fn, toolResult, t1 } of safeResults) { // :1578 ← tc does not exist here\n ...\n messages.push({ role:&#x27;tool&#x27;, tool_call_id: tc.id, content: toolResult }); // :1615 💥\n\n\ntc is undeclared in pass 2 → ReferenceError. Classes are strict-mode, so it&#x27;s a hard throw — on every tool-using AI turn, after the tool already ran. The tool result is never appended to the conversation, so the model never sees it and the turn aborts.\n\nThe error surfaced as Security: AI error: tc is not defined because terminal.js:266 — the catch inside WebX11SecurityManager._tryAIRoute() — prefixes every exception with Security: AI error:.\n\nFix (2 lines)\ntc is now carried through safeResults, so tool_call_id: tc.id preserves the id the model issued.\n\nVerification\n\nNew static gate — scripts/lint-no-undef.py (ESLint no-undef, auto-collects 118 cross-file globals so only genuine undefineds report):\n\nfixed file\n• Result: ✅ clean\n\nreverted copy (gate validation)\n• Result: 1615:73 error &#x27;tc&#x27; is not defined\n\nwhole core+ext, 55 files\n• Result: ✅ clean\n\nLive browser, prod bundle v4.6.42 — stubbed the model, kept the real loop:\n{\&quot;calls\&quot;: 2, \&quot;ok\&quot;: true,\n \&quot;printedToolLine\&quot;: \&quot;🔧 Tool: list_files({})\&quot;,\n \&quot;securityAiError\&quot;: null,\n \&quot;toolMessagesSeenByModel\&quot;: [{\&quot;role\&quot;:\&quot;tool\&quot;,\&quot;id\&quot;:\&quot;call_test_1\&quot;,\&quot;content\&quot;:\&quot;STUB_RESULT:list_files\&quot;}]}\n\nThe crashing statement now executes and the model receives the tool result with its original tool_call_id.\n\nRegression — node scripts/test-agents-lifecycle.js → ALL CHECKS PASSED. Page console: js_errors: [].\n\nShipped\nv4.6.41 → v4.6.42 — bundle rebuilt, cache-bust in main.html (3 tags) + module-loader.js, served over HTTPS confirmed.\n\nArtifacts\nscripts/lint-no-undef.py\n• Purpose: scope gate — run before every rebuild\n\nadmin/docs/newos4-ai-tool-loop-tc-undefined-2026-10-18.md\n• Purpose: full RCA\n\nskill newos4-module-patch-and-verify (129 lines)\n• Purpose: updated: gate in workflow, 2 new pitfalls, AI tool-loop probe recipe\n\nBootstrap the linter once: mkdir -p /tmp/lint &amp;&amp; cd /tmp/lint &amp;&amp; npm i eslint@8 (or set ESLINT_BIN).\n\nOne thing worth flagging: this bug existed in v4.6.41 too — the version bump just made your browser fetch fresh code, which is why it looked \&quot;new.\&quot; It would have hit the first time you asked the AI to use a tool, regardless.&quot;,
+ &quot;ollamaconnect&quot;,
+ &quot;✅ tc is not defined — found, fixed, verified\n\nSame bug family as the /agents/ leak: a refactor moved code, an identifier didn&#x27;t move with it, and a broad catch buried the evidence.\n\nRoot cause — module-aiassist.js, chat() tool executor\n\nThe \&quot;execute non-destructive tools sequentially\&quot; refactor split one loop into two passes:\n\nfor (const tc of safeTools) {\n ...\n safeResults.push({ fn, toolResult, t1 }); // :1576 ← tc not carried over\n}\n\nfor (const { fn, toolResult, t1 } of safeResults) { // :1578 ← tc does not exist here\n ...\n messages.push({ role:&#x27;tool&#x27;, tool_call_id: tc.id, content: toolResult }); // :1615 💥\n\n\ntc is undeclared in pass 2 → ReferenceError. Classes are strict-mode, so it&#x27;s a hard throw — on every tool-using AI turn, after the tool already ran. The tool result is never appended to the conversation, so the model never sees it and the turn aborts.\n\nThe error surfaced as Security: AI error: tc is not defined because terminal.js:266 — the catch inside WebX11SecurityManager._tryAIRoute() — prefixes every exception with Security: AI error:.\n\nFix (2 lines)\ntc is now carried through safeResults, so tool_call_id: tc.id preserves the id the model issued.\n\nVerification\n\nNew static gate — scripts/lint-no-undef.py (ESLint no-undef, auto-collects 118 cross-file globals so only genuine undefineds report):\n\nfixed file\n• Result: ✅ clean\n\nreverted copy (gate validation)\n• Result: 1615:73 error &#x27;tc&#x27;
+… (truncated)</pre>
+ </details>
+ </article>
+ <article class="card">
+ <header>
+ <h3>Auto: [WorldModel] Save failed: temp file corrupt after write</h3>
+ <span class="pill">RPT-20261018-161111-c93a18ef</span>
+ </header>
+ <p class="meta">2026-10-18T16:11:11+00:00 · version v4.6.40 · 1 error(s)</p>
+ <p class="body">Automatically filed by the debug supervisor (no user action).</p>
+ <details>
+ <summary>Diagnostics</summary>
+ <pre>{
+ &quot;version&quot;: &quot;v4.6.40&quot;,
+ &quot;url&quot;: &quot;https://webx11.duckdns.org/spaces/newos4/main.html&quot;,
+ &quot;ua&quot;: &quot;Mozilla/5.0 (Linux; Android 12; SM-A125F Build/SP1A.210812.016) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.88 Mobile Safari/537.36&quot;,
+ &quot;auto&quot;: true,
+ &quot;errors&quot;: [
+ {
+ &quot;message&quot;: &quot;[WorldModel] Save failed: temp file corrupt after write&quot;,
+ &quot;source&quot;: &quot;console.error&quot;,
+ &quot;type&quot;: &quot;runtime&quot;,
+ &quot;severity&quot;: &quot;error&quot;,
+ &quot;count&quot;: 1
+ }
+ ]
+}</pre>
+ </details>
+ </article>
</main>
</body>
</html>
diff --git a/main.html b/main.html
index 9701164..73f4b0b 100644
--- a/main.html
+++ b/main.html
@@ -1349,13 +1349,13 @@
</script>
<!-- v4.5.63: A2A scratchpad fixes + agent-register arg fix -->
-<script src="modules/core/core-bundle.js?v=v4.6.53"></script>
+<script src="modules/core/core-bundle.js?v=v4.6.54"></script>
<!-- Async dependency-ordered module loader now only initializes terminal and loads optional ext modules -->
-<script src="modules/core/module-loader.js?v=v4.6.53"></script>
+<script src="modules/core/module-loader.js?v=v4.6.54"></script>
<!-- Critical ext bundle loads AFTER webx11 exists so registerModule calls work -->
-<script src="modules/ext/critical-ext-bundle.js?v=v4.6.53"></script>
+<script src="modules/ext/critical-ext-bundle.js?v=v4.6.54"></script>
<script>
// ═══ ONE TRUE VERSION SOURCE ═══
diff --git a/modules/core/core-bundle.js b/modules/core/core-bundle.js
index 331e090..212573d 100644
--- a/modules/core/core-bundle.js
+++ b/modules/core/core-bundle.js
@@ -632,6 +632,19 @@ class VirtualFileSystem {
}
return false;
}
+ // Reported 2026-10-18 ("this.fs.emptyTrash is not a function") and verified 2026-10-23:
+ // terminal.js:1777 called this method but nothing ever defined it, so `empty-trash --confirm`
+ // threw instead of emptying. Returns true only when something was actually removed.
+ emptyTrash() {
+ const trash = this.root.content && this.root.content['.trash'];
+ if (!trash || !trash.content) return false;
+ const names = Object.keys(trash.content);
+ if (!names.length) return false;
+ names.forEach(n => { delete trash.content[n]; });
+ this.saveToStorage();
+ this._emitChange('/.trash');
+ return true;
+ }
_pruneTrash(maxEntries = 50) {
// Bounded trash: deletions are moves-to-trash, so without a cap / .trash/ was an
// unbounded second accumulator. Names start with a millis timestamp → sort = oldest first.
diff --git a/modules/core/module-loader.js b/modules/core/module-loader.js
index f22576a..a3b3a6f 100644
--- a/modules/core/module-loader.js
+++ b/modules/core/module-loader.js
@@ -4,7 +4,7 @@
// Core module bundle loaded synchronously in main.html.
// This loader only handles version cache-busting, splash, terminal init, and optional ext modules.
- const LATEST_VER = 'v4.6.53';
+ const LATEST_VER = 'v4.6.54';
const CACHE = '?v=' + LATEST_VER;
const stored = localStorage.getItem('webx11_cache_ver');
if (stored !== LATEST_VER) {
diff --git a/modules/core/virtual-fs.js b/modules/core/virtual-fs.js
index 8f11d25..c9eb7a3 100644
--- a/modules/core/virtual-fs.js
+++ b/modules/core/virtual-fs.js
@@ -595,6 +595,7 @@ class VirtualFileSystem {
const trash = this.root.content['.trash'];
const trashName = ts + '-' + name;
trash.content[trashName] = JSON.parse(JSON.stringify(node));
+ this._pruneTrash();
const resolved = this._resolvePath(path);
if (resolved) delete resolved.dir.content[resolved.name];
this.saveToStorage();
@@ -620,6 +621,7 @@ class VirtualFileSystem {
let ctr = 0;
while (trash.content[unique]) { unique = baseName + '-' + (++ctr); }
trash.content[unique] = JSON.parse(JSON.stringify(node));
+ this._pruneTrash();
delete resolved.dir.content[resolved.name];
this.saveToStorage();
this._emitChange(path);
@@ -628,6 +630,31 @@ class VirtualFileSystem {
}
return false;
}
+ // Reported 2026-10-18 ("this.fs.emptyTrash is not a function") and verified 2026-10-23:
+ // terminal.js:1777 called this method but nothing ever defined it, so `empty-trash --confirm`
+ // threw instead of emptying. Returns true only when something was actually removed.
+ emptyTrash() {
+ const trash = this.root.content && this.root.content['.trash'];
+ if (!trash || !trash.content) return false;
+ const names = Object.keys(trash.content);
+ if (!names.length) return false;
+ names.forEach(n => { delete trash.content[n]; });
+ this.saveToStorage();
+ this._emitChange('/.trash');
+ return true;
+ }
+ _pruneTrash(maxEntries = 50) {
+ // Bounded trash: deletions are moves-to-trash, so without a cap / .trash/ was an
+ // unbounded second accumulator. Names start with a millis timestamp → sort = oldest first.
+ const trash = this.root.content['.trash'];
+ if (!trash) return 0;
+ const keys = Object.keys(trash.content);
+ if (keys.length <= maxEntries) return 0;
+ keys.sort();
+ const drop = keys.slice(0, keys.length - maxEntries);
+ drop.forEach(k => { delete trash.content[k]; });
+ return drop.length;
+ }
deleteDirRecursive(path) {
return this.trashDir(path);
}