<feed xmlns='http://www.w3.org/2005/Atom'>
<title>newos4, branch v4.6.151</title>
<subtitle>newOS4 - the browser terminal that runs WebX11 user spaces</subtitle>
<id>https://newos.webx11.duckdns.org/newos4/atom?h=v4.6.151</id>
<link rel='self' href='https://newos.webx11.duckdns.org/newos4/atom?h=v4.6.151'/>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/'/>
<updated>2026-11-06T15:19:59+00:00</updated>
<entry>
<title>release: v4.6.151 — wget that actually saves, web_fetch that actually pages</title>
<updated>2026-11-06T15:19:59+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-11-06T15:19:59+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=0cb252dfbeb02d23cd92bad3ca8df76be22d9f92'/>
<id>urn:sha1:0cb252dfbeb02d23cd92bad3ca8df76be22d9f92</id>
<content type='text'>
S1 (reported by agent-os4-maint-01, wrongly refuted by the chat auto-reply):
wget existed but was unusable and dishonest.
- Cause 1: _cmdWget used the page's own fetch(url), so every cross-origin URL
  died on CORS ("Failed to fetch"). Measured in the served v4.6.150 page:
  fetch(cgit README) threw, /api/fetch returned 200/1954 bytes.
- Cause 2: _vfsWriteBytes ignored writeFile()'s boolean, so the command printed
  a green tick when the target directory did not exist. Measured:
  fs.writeFile('nosuchdir/x.txt') === false.
- Cause 3: bytes were round-tripped through String.fromCharCode, mangling UTF-8.
- Fix: fetch through /api/fetch (raw source), mkdir -p, writeFile + read-back
  verification, error instead of a tick on failure. Register curl with
  -s/-o parsing delegating to the same writer so the two cannot drift.

S2 (same reporter): web_fetch could not page and said it could.
- Cause: the handler honoured max_chars up to 50 000, then _clipToolResult kept
  toolResultChars-200 = 11 800, so 38 200 chars vanished silently; offset was
  not in the schema and was never read, so next_offset pointed at a page the
  caller could not request again.
- Fix: offset/next_offset/total_chars/source_bytes in the contract; the page is
  built, measured against the clip budget and shrunk until the envelope fits,
  so next_offset - returned_chars is always 0.
- Also fixed: _clipToolResult appended "0 of N chars withheld" when the content
  already fitted, claiming a loss that did not happen.

C1: fetch &lt;url&gt; --save=FILE — the only terminal path from a remote URL to the
VFS, so a 90 KB source page no longer has to be read through 3 000-char
scrollback.

Proxy: /api/extract now returns source_bytes (raw page size) and
content_chars_utf16. Python len() counts code points while JS String.length
counts UTF-16 units; 7 astral characters in the changelog left a 7-char gap at
the end of a 90 KB page (measured). _html_ok declares charset=utf-8.

Verified: node --check on all four modules; py_compile; bundles rebuilt;
served bytes identical to disk for all five assets at ?v=v4.6.151; live in the
browser: curl -s -o review/main.md &lt;cross-origin&gt; saved 1954 chars with the
correct head, a 404 wrote no file, fetch --save wrote 91 518 chars,
web_fetch p1 returned 11 754 with next_offset 11 754 (gap 0), page 2 started at
a different offset with next 23 496.
</content>
</entry>
<entry>
<title>chore(changelog): regenerate for v4.6.150</title>
<updated>2026-11-06T10:45:26+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-11-06T10:45:26+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=65b28012ed3aa492604310b43fc499cc13a592c6'/>
<id>urn:sha1:65b28012ed3aa492604310b43fc499cc13a592c6</id>
<content type='text'>
</content>
</entry>
<entry>
<title>release(v4.6.150): commit the module work that has been live but uncommitted</title>
<updated>2026-11-06T10:45:21+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-11-06T10:45:21+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=03143a6e88832d06c7b08cdbf8de96ba79bda7d6'/>
<id>urn:sha1:03143a6e88832d06c7b08cdbf8de96ba79bda7d6</id>
<content type='text'>
Operator commit-approve, 2026-11-06. These changes have been served since
2026-11-02/03 while git HEAD still stamped v4.6.146, so the mirror published a
tree four builds behind the running instance and the release tag named the
wrong build.

module-orchestrator.js — orchestrate-parallel reported success it could not
prove. Results are now recorded by sub-task id through a resultSink handed to
delegateTask (harvest matched by currentTask/lastTask strings, which a later
delegation on the same agent overwrote, losing finished work as "agent not
found"); the tally counts summaries with status ok instead of
Promise.allSettled resolutions (a spawn refusal fulfils with undefined and used
to print green); and a busy agent is no longer reused at pool cap, where two
siblings erased each other's lease. Guard:
modules/test/test-orchestrator-parallel-harvest.js (16 cases, reproduces the
one-agent three-sub-task cap case that printed 3/3 succeeded).

module-billing.js — the balance said "not signed in — newOS4 sends no session
token", which was already false: the browser sends the HttpOnly cookie and
parse_bearer_token() falls back to it. `cost` is on by default (the flag was
opt-in and nobody knew the command existed; `cost off` opts out) and the
command now prints today's platform spend from GET /api/billing/usage?days=1
alongside the session estimate.

modules/ext/webdav.js — the inline WebView detector missed "Version/4.0"
Android WebViews, including the operator's own phone, so the login offered a
Telegram widget that cannot complete there. It now delegates to
modules/wx11-tg-webview.js when the page loaded it and keeps a matching
fallback (wv token, Version/4.0-Android, Telegram, FBAN/Instagram/Line).

modules/ext/media.js — text analysis model renamed deepseek-v4-flash -&gt;
deepseek-v4.1-flash in both the field and the printed label.

main.html + module-loader.js — v4.6.146 -&gt; v4.6.150 in LATEST_VER and the three
asset queries; core-bundle.js rebuilt (rebuild-bundles.py reports it already
in sync, 1,250,569 bytes).

Verified: node --check on all six scripts and the new test; the 16-case
orchestrator guard passes against the deployed bundle (ORCH_SRC=...); bundle
rebuild is a no-op; core-bundle.js?v=v4.6.150 answers 200.
</content>
</entry>
<entry>
<title>chore(changelog): regenerate after the render and ordering fixes</title>
<updated>2026-11-06T10:41:00+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-11-06T10:41:00+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=15b42d6379484bb3f439eb5d7f77f12877f74497'/>
<id>urn:sha1:15b42d6379484bb3f439eb5d7f77f12877f74497</id>
<content type='text'>
</content>
</entry>
<entry>
<title>fix(changelog): keep the newest commits of a build; wrap long detail URLs</title>
<updated>2026-11-06T10:40:54+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-11-06T10:40:54+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=7bdcd58762c60c9501657b9df2983999f276f8b1'/>
<id>urn:sha1:7bdcd58762c60c9501657b9df2983999f276f8b1</id>
<content type='text'>
Symptom: after bodies were rendered, this fix's own commit was missing from
its own entry, and a long URL inside an expanded detail was clipped at the
right edge on a 390 px screen.

Cause: items took group[:MAX_ITEMS] from a CHRONOLOGICAL list, so a 17-commit
build showed its first 14 and dropped the newest three (the count line said
"…and 3 more", hiding which three). And the injected detail CSS set no
overflow-wrap, so a 47-character URL pushed the paragraph to 320 px of
unbreakable text — the document stayed at 0 overflow only because the li
clipped it.

Fix: show group[-MAX_ITEMS:] and label the omitted ones "…and N earlier
commit(s)"; entry.commits follows the same slice. overflow-wrap:anywhere on
detail p/ul. The CSS block is now re-emitted from DETAIL_CSS behind
BEGIN/END sentinels instead of being write-once, and the first version's
sentinel-less block is stripped on sight so no page keeps two copies.

Verified: 390 px — p scrollWidth == clientWidth == 320, document overflow 0,
URL wraps; newest entry lists c759c50 and this commit; island 83 entries,
14 commit chips matching 14 shown items; CHANGELOG.md byte-identical on a
second run.
</content>
</entry>
<entry>
<title>fix(changelog): render the commit body, not just the subject</title>
<updated>2026-11-06T10:37:11+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-11-06T10:37:11+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=1983ce5796e45b312b142554cb717db7f8978bc8'/>
<id>urn:sha1:1983ce5796e45b312b142554cb717db7f8978bc8</id>
<content type='text'>
Symptom: the mirrored CHANGELOG.md (and the page) showed one-line commit
summaries while README promises "every shipped fix, symptom -&gt; cause -&gt; fix -&gt;
verified". A reviewer measured the gap from the public mirror and could only
conclude the claim overpromised.

Cause: scripts/build-changelog.py:59 formatted commits with %s only; %b was
never read, so the verification prose every fix commit carries (5b55af9 is a
typical one) was discarded before rendering. The MD writer then replaced every
tag with a backtick, so nothing richer could have survived anyway.

Fix: read %b with a %x02 terminator (line-based parser, multi-line body), split
it into paragraph/bullet blocks capped at 900 chars with a visible marker, and
render it twice from one source: as a native &lt;details&gt; per item on the page
(its renderer already assigns item HTML, so no JS change; CSS injected once
behind a sentinel) and as nested bullets under the line in CHANGELOG.md. The
"every build" gap note and the page dek were rewritten to describe what the
file now contains.

Verified: page 100 details render, first opens 23 -&gt; 232 px with 3 paragraphs,
no horizontal overflow, island parses (83 entries); two consecutive runs are
byte-identical for CHANGELOG.md; the double-escape bug this surfaced (code
spans shipped as &amp;amp;lt;sha&amp;amp;gt;) is fixed and grep for &amp;amp;lt; is 0.
</content>
</entry>
<entry>
<title>feat(auth): self-service git push tokens from the dashboard</title>
<updated>2026-11-05T22:49:03+00:00</updated>
<author>
<name>WebX11 SysOp</name>
<email>darkmotox11@gmail.com</email>
</author>
<published>2026-11-05T22:49:03+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=d5dc2e1cb2f1463d001c3b10d1637298997f8559'/>
<id>urn:sha1:d5dc2e1cb2f1463d001c3b10d1637298997f8559</id>
<content type='text'>
GET/POST /api/auth/gitpush-token (+ /revoke) on auth-api: session-scoped, CSRF-gated,
3 mints per account per hour, bcrypt into the htpasswd, plaintext returned once and
never stored, audit at /var/lib/webx11/git-push-audit.jsonl.

No sudo or reload needed: nginx re-opens auth_basic_user_file per request, so a new
line is live immediately — proved by a 500 whose error line was per request.

Dashboard card 'Git push token' shows none/active, mints and reveals the remote once,
revokes. Verified in a real browser: badge none -&gt; mint -&gt; active, remote shape checked,
then revoked; htpasswd back to 0 bytes and the test session removed.
</content>
</entry>
<entry>
<title>feat(git): namespace contrib refs per account (contrib/&lt;user&gt;/&lt;topic&gt;)</title>
<updated>2026-11-05T21:32:04+00:00</updated>
<author>
<name>WebX11 SysOp</name>
<email>darkmotox11@gmail.com</email>
</author>
<published>2026-11-05T21:32:04+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=ab7dcab65739b2791595d80012b207542a7b5171'/>
<id>urn:sha1:ab7dcab65739b2791595d80012b207542a7b5171</id>
<content type='text'>
Done before anyone has a token or a branch, because renaming later breaks every
contributor. The hook compares the middle path segment to REMOTE_USER, so a token
can only write under its own namespace; deletion and cleanup become a prefix
operation instead of a guess.

Verified 4/4: own namespace accepted, neighbour's namespace refused naming the
authenticated user, missing owner segment refused with the correct form, main
refused. Test branch and token removed afterwards; queue back to 0.
</content>
</entry>
<entry>
<title>feat(git): contrib push surface with automated moderation</title>
<updated>2026-11-05T21:26:25+00:00</updated>
<author>
<name>WebX11 SysOp</name>
<email>darkmotox11@gmail.com</email>
</author>
<published>2026-11-05T21:26:25+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=28c29f2c270009a1d48857afe05993ff6322d0ff'/>
<id>urn:sha1:28c29f2c270009a1d48857afe05993ff6322d0ff</id>
<content type='text'>
Users push to push.newos.webx11.duckdns.org with a per-user token; the read-only
host is untouched. http.receivepack is unset so git only allows receive-pack when
REMOTE_USER is present, which only that vhost sets. A pre-receive hook (installed
by the publisher every run) allows refs/heads/contrib/* only and refuses banned
paths, credential-shaped blobs, &gt;200 files or &gt;2MB, logging user+ref+decision.

moderate-contrib.py triages every 10 min: deletes hard violations, stamps the
rest ready-docs/review/stale into a queue rendered at /admin/git-contrib.html.
A human still decides what merges.

Verified end to end: contrib push accepted, main refused, credential file
refused, accepted ref visible on the read-only host; guard extended (push host
401, hook present+executable, receivepack unset) and proven RED by removing the
hook.
</content>
</entry>
<entry>
<title>docs(newos4): reconcile pi version (verified on 0.80.3, npm ships 1.5.0)</title>
<updated>2026-11-05T21:16:10+00:00</updated>
<author>
<name>WebX11 SysOp</name>
<email>darkmotox11@gmail.com</email>
</author>
<published>2026-11-05T21:16:10+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=7d14ce41d6a24d8a20b34586a657cec498348fc7'/>
<id>urn:sha1:7d14ce41d6a24d8a20b34586a657cec498348fc7</id>
<content type='text'>
</content>
</entry>
</feed>
