Files
gex/web/package.json
velzieandClaude Opus 5 ea17d79c94 A demo page, and the browser features it needed
web/ is a Vite app with xterm.js and three demos: run a binary you supply,
a BusyBox userspace with a shell in it, and Debian with apt (not finished --
it needs the network layer). Getting the first two working meant four
things the browser host did not have.

`gexc seed` writes a module that translates nothing: the entry stub, the
miss stub, an empty dispatch table. Every branch it takes misses, so the
runtime translator behind it produces the whole program -- which makes one
1.4 KB module enough to run *any* guest. A page cannot run the
ahead-of-time compiler, so without this a page can only run programs
somebody compiled for it in advance. It runs a static hello in 0.2s and
busybox in 0.3s.

Interactive input. A read on an empty stream is now a BlockOp rather than a
short read, decided before the call by asking the handle whether a read
would wait -- which is different from turning an EAGAIN into a wait
afterwards, because a descriptor opened O_NONBLOCK is supposed to see the
EAGAIN. The browser scheduler parks the thread, and when every thread is
waiting it gives the wasm stack back to JS, which is the only place a page
can wait for a keystroke at all.

Termios is real. TCSETS used to return success and discard what it set, so
a shell that turned off ICANON and ECHO believed it had taken over line
editing while the page was still doing its own, and every keystroke landed
twice. The kernel keeps the struct and the page follows it.

fork and exec in the browser, which the outcome match used to answer with
"unsupported". fork is done by checkpoint: the parent's memory, kernel,
threads and dispatch state are copied aside, the child runs in place, and
the parent is restored when it exits with the child's pid already in x0.
The parent cannot run while the child does; for fork-then-wait, which is
every shell, that is invisible.

That last one took a real bug to get right. Pipelines crashed with a jump
to an unaligned stack address. Restoring the parent restored its dispatch
page table too -- including the slot allocation mark -- so the next
translation handed out slots the child had already filled, and a page whose
directory entry survived the restore dispatched into another page's code.
Slots are now monotonic across a checkpoint: the parent keeps its pages,
allocation carries on from wherever the child got to.

`ls /bin | head -4`, `echo a b c | wc -w` and a for loop all work in an
interactive shell in the browser runtime. 89 stages pass.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01KcidnrZ5aLqKoBJBqdjx9d
2026-08-06 00:47:05 -04:00

25 lines
510 B
JSON

{
"name": "web",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"assets": "./build-assets.sh"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "module",
"devDependencies": {
"vite": "^8.2.0"
},
"dependencies": {
"@mercuryworkshop/wisp-js": "^0.4.1",
"@xterm/addon-fit": "^0.11.0",
"@xterm/addon-web-links": "^0.12.0",
"@xterm/xterm": "^6.0.0"
}
}