aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-10-23 18:25:52 +0200
committerAgando <roduccomm@gmail.com>2026-10-23 18:25:52 +0200
commit9c27d3790b594c94430015675c91ce618c61be1d (patch)
treed7f8be0f194d3bb8346a77d9e4b63b53d41200d7
parent2bc11203b73fe2eb210ce3dd643773e625609c25 (diff)
fix(newos4): an invalid grep pattern returns a message, not an exception (v4.6.64)
Packet 8. `new RegExp(pattern)` sat unguarded in the terminal grep handler and in VFS find()/grep(), so a bad pattern threw out of the command handler into the error ledger -- the same ledger packet 2 had to stop trusting. - module-fs.js grep: compile in try/catch, print "grep: invalid pattern: <pattern> (<engine message>)" as a warning and return - virtual-fs.js find()/grep(): same guard, return [] and stash the message on _lastPatternError instead of throwing into the module error wrapper Verified live on v4.6.64 with the packet's own repro: `grep "/\bug_id\" file` prints the message, the error counter stays 0 (was: one ledger entry per call), a valid pattern still matches, and fs.grep('[') returns [] instead of throwing.
-rw-r--r--help.html3
-rw-r--r--main.html6
-rw-r--r--modules/core/core-bundle.js21
-rw-r--r--modules/core/module-fs.js11
-rw-r--r--modules/core/module-loader.js2
-rw-r--r--modules/core/virtual-fs.js10
6 files changed, 42 insertions, 11 deletions
diff --git a/help.html b/help.html
index 0937251..00eff18 100644
--- a/help.html
+++ b/help.html
@@ -1689,13 +1689,14 @@ listmods</pre>
<h2>๐Ÿ“‹ Changelog</h2>
<div class="version-item">
- <div class="v-header"><span class="v-num">v4.6.57โ€“63 โ€” October 2026</span><span class="v-date">2026-10-23</span></div>
+ <div class="v-header"><span class="v-num">v4.6.57โ€“64 โ€” October 2026</span><span class="v-date">2026-10-23</span></div>
<ul>
<li><strong>No shell โ€” and it now says so</strong> โ€” <code>;</code>, <code>&amp;&amp;</code> and <code>||</code> are refused with an explicit message instead of being folded into the command's argv (where <code>wc -l f &amp;&amp; grep -c x f</code> returned a plausible wrong number). A <code>|</code> arriving from the AI <code>run_command</code> tool is refused too, because that path bypasses the pipe splitter and degraded the pattern to empty. Documented at the top of the command reference.</li>
<li><strong>Multiple paths in <code>wc</code></strong> โ€” <code>wc -c a b</code> printed only <code>a</code> and exited clean. It now prints one line per file plus a <code>total</code>, and reports each missing file separately.</li>
<li><strong><code>ls</code> prints text</strong> โ€” it rendered only clickable chips (DOM-only), so any capture โ€” the AI <code>run_command</code> tool, the diagnostics sweep โ€” saw an empty result and reported "directory empty".</li>
<li><strong><code>view_module</code> shows what is deployed</strong> โ€” it returned the <code>_mods/&lt;Class&gt;.js</code> edit cache first, unversioned, so after a rebuild it showed pre-fix source while the bundle was new. It now fetches the deployed file and prefixes the version; the cache is a labelled fallback.</li>
<li><strong><code>load_skills</code> searches tokens, not one phrase</strong> โ€” the query used to be tested whole against name+description+tags, so <code>"telemetry dashboard"</code> returned 0 while <code>"telemetry"</code> returned 1, and a sentence could never match. Skill <em>bodies</em> were never searched. It now tokenises (stopwords dropped), scores name &gt; description/tags &gt; body, weights by how many tokens matched, keeps a phrase bonus so word order cannot zero a real hit, and reports <code>matchedTokens: 2/3</code> with each result.</li>
+ <li><strong>An invalid <code>grep</code> pattern is an answer, not a crash</strong> โ€” it used to throw out of the command handler into the error ledger, which is the ledger that has to stay trustworthy. It now prints <code>grep: invalid pattern: โ€ฆ</code> and returns.</li>
<li><strong>Agent status says what it means</strong> โ€” four agents showed <code>sleeping</code> with no way to tell "waiting for work" from "never handed a task". They now read <code>parked โ€” NEVER SCHEDULED (0 tasks since โ€ฆ)</code> or <code>waiting for work (3 tasks, last โ€ฆ)</code>, plus a dispatch line and a warning when spawning happened but nothing was ever dispatched.</li>
<li><strong>A guide you can actually find</strong> โ€” <code>guides</code> lists <code>/guides/</code> newest-first; the help page has a Guides section with the command, the preview/cat hints and links to published guides.</li>
<li><strong>One error counter</strong> โ€” <code>errors</code>, <code>get_dashboard_data</code> and the world model tallied three different sinks (0 / 18 / 11 in the same second). All three now read <code>terminal.countErrors()</code> (DebugSupervisor DB + failed tool calls); the VFS log is reported separately as a mirror, and <code>terminal.log('ERROR', โ€ฆ)</code> now records into the same DB instead of a second store nobody read.</li>
diff --git a/main.html b/main.html
index a76b3d1..79c9b71 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.63"></script>
+<script src="modules/core/core-bundle.js?v=v4.6.64"></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.63"></script>
+<script src="modules/core/module-loader.js?v=v4.6.64"></script>
<!-- Critical ext bundle loads AFTER webx11 exists so registerModule calls work -->
-<script src="modules/ext/critical-ext-bundle.js?v=v4.6.63"></script>
+<script src="modules/ext/critical-ext-bundle.js?v=v4.6.64"></script>
<script>
// โ•โ•โ• ONE TRUE VERSION SOURCE โ•โ•โ•
diff --git a/modules/core/core-bundle.js b/modules/core/core-bundle.js
index df4563d..a5e9a79 100644
--- a/modules/core/core-bundle.js
+++ b/modules/core/core-bundle.js
@@ -824,7 +824,10 @@ class VirtualFileSystem {
dir = resolved.dir;
}
const results = [];
- const regex = new RegExp(pattern, 'i');
+ // v4.6.64: an invalid pattern must not throw into the error ledger (packet ยง8).
+ let regex;
+ try { regex = new RegExp(pattern, 'i'); }
+ catch (e) { this._lastPatternError = e.message; return []; }
for (const name of Object.keys(dir.content)) {
const fullPath = path ? path + '/' + name : name;
if (regex.test(name)) {
@@ -838,7 +841,10 @@ class VirtualFileSystem {
}
grep(pattern, path = '') {
const results = [];
- const regex = new RegExp(pattern, 'i');
+ // v4.6.64: see find() above โ€” a bad pattern is a result, not an exception.
+ let regex;
+ try { regex = new RegExp(pattern, 'i'); }
+ catch (e) { this._lastPatternError = e.message; return []; }
const files = this.find('.', path).filter(e => e.type === 'file');
for (const file of files) {
const content = this.readFile(file.path);
@@ -6636,7 +6642,16 @@ class FileSystemModule {
}
const pattern = args[ai]; const target = args[ai + 1];
if (!pattern) return this.terminal.print('Usage: grep [-n] [-i] [-c] [-l] <pattern> [filename]', 'warning');
- const re = new RegExp(pattern, opt.ignoreCase ? 'i' : '');
+ // v4.6.64: an invalid pattern is user input, not a system fault. It used to throw
+ // out of the command handler into the error ledger, polluting the counter that
+ // packet ยง2 says nobody can trust (packet ยง8).
+ let re;
+ try {
+ re = new RegExp(pattern, opt.ignoreCase ? 'i' : '');
+ } catch (e) {
+ this.terminal.print('grep: invalid pattern: ' + pattern + ' (' + e.message + ')', 'warning');
+ return;
+ }
const hits = (line) => (opt.invert ? !re.test(line) : re.test(line));
if (target) {
const c = this.terminal.fs.readFile(target);
diff --git a/modules/core/module-fs.js b/modules/core/module-fs.js
index fd1dfc1..415bd92 100644
--- a/modules/core/module-fs.js
+++ b/modules/core/module-fs.js
@@ -238,7 +238,16 @@ class FileSystemModule {
}
const pattern = args[ai]; const target = args[ai + 1];
if (!pattern) return this.terminal.print('Usage: grep [-n] [-i] [-c] [-l] <pattern> [filename]', 'warning');
- const re = new RegExp(pattern, opt.ignoreCase ? 'i' : '');
+ // v4.6.64: an invalid pattern is user input, not a system fault. It used to throw
+ // out of the command handler into the error ledger, polluting the counter that
+ // packet ยง2 says nobody can trust (packet ยง8).
+ let re;
+ try {
+ re = new RegExp(pattern, opt.ignoreCase ? 'i' : '');
+ } catch (e) {
+ this.terminal.print('grep: invalid pattern: ' + pattern + ' (' + e.message + ')', 'warning');
+ return;
+ }
const hits = (line) => (opt.invert ? !re.test(line) : re.test(line));
if (target) {
const c = this.terminal.fs.readFile(target);
diff --git a/modules/core/module-loader.js b/modules/core/module-loader.js
index 5bf2a4c..abc1a86 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.63';
+ const LATEST_VER = 'v4.6.64';
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 c9eb7a3..a52a393 100644
--- a/modules/core/virtual-fs.js
+++ b/modules/core/virtual-fs.js
@@ -822,7 +822,10 @@ class VirtualFileSystem {
dir = resolved.dir;
}
const results = [];
- const regex = new RegExp(pattern, 'i');
+ // v4.6.64: an invalid pattern must not throw into the error ledger (packet ยง8).
+ let regex;
+ try { regex = new RegExp(pattern, 'i'); }
+ catch (e) { this._lastPatternError = e.message; return []; }
for (const name of Object.keys(dir.content)) {
const fullPath = path ? path + '/' + name : name;
if (regex.test(name)) {
@@ -836,7 +839,10 @@ class VirtualFileSystem {
}
grep(pattern, path = '') {
const results = [];
- const regex = new RegExp(pattern, 'i');
+ // v4.6.64: see find() above โ€” a bad pattern is a result, not an exception.
+ let regex;
+ try { regex = new RegExp(pattern, 'i'); }
+ catch (e) { this._lastPatternError = e.message; return []; }
const files = this.find('.', path).filter(e => e.type === 'file');
for (const file of files) {
const content = this.readFile(file.path);