buninu 0.4.6 โ†’ 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,29 +1,45 @@
1
1
  # Buninu
2
2
 
3
+ **BUNinu Is Not Unix** ๐Ÿฎ
4
+
5
+ **ๅนซไฝ ็‰›** ๐Ÿ‚ ใƒป **Bunใซๅ…ฅ้ญ‚** ๐Ÿ”ฅ
6
+
3
7
  ![icon](https://raw.githubusercontent.com/jjtseng93/buninu/main/icon.png)
4
8
 
5
- > **Early development:** Buninu is still at an early stage of development.
6
- > Features, configuration, and command-line behavior may change between releases.
7
- > Linux, Android, and Windows can run the current early implementation, but
8
- > their support and portability behavior are still being tested and refined.
9
+ Buninu is a portable, self-bootstrapping Unix-like userspace built on Bun. One
10
+ command gives you a working shell โ€” in a browser tab, or straight in the
11
+ terminal you are already in โ€” on Android, Linux, Windows, and macOS, with
12
+ nothing to compile and nothing installed system-wide.
9
13
 
10
- - Buninu is a portable, self-bootstrapping Unix-like userspace built on Bun that runs across operating systems
14
+ ```sh
15
+ npx buninu
16
+ ```
11
17
 
12
- - It starts a browser-accessible terminal through jsgotty. The architecture is
13
- designed for Android, Linux, and Windows.
18
+ > **Status:** Buninu is still under active development. The browser
19
+ > terminal, the install-and-update flow, and the bundled commands are the
20
+ > settled parts โ€” Android and Linux first, with Windows working the same way
21
+ > and macOS support newer. Still moving: `package.json` settings and
22
+ > command-line flags can change between releases, and the local shell
23
+ > (`--local`, bunmsh) has no job control yet, so use the browser terminal when
24
+ > you need to background a job.
14
25
 
15
- - Name expansions:
16
- * English: **BUNinu Is Not Unix** ๐Ÿฎ
17
- * ไธญๆ–‡๏ผš**ๅนซไฝ ็‰›** ๐Ÿ‚
18
- * ๆ—ฅๆœฌ่ชž๏ผš**Bunใซๅ…ฅ้ญ‚** ๐Ÿ”ฅ
26
+ - Source: [github.com/jjtseng93/buninu](https://github.com/jjtseng93/buninu)
19
27
 
20
28
  - Core components:
21
29
  * [jsgotty](https://github.com/jjtseng93/js-gotty): Remote shell from a Browser or Terminal
22
- * [jsmdcui](https://github.com/jjtseng93/jsmdcui): Both a text editor and Markdown execution runtime (not static rendering) based on [bunmicro](https://github.com/jjtseng93/bunmicro)
23
- * [bunmsh](https://github.com/jjtseng93/bunmsh): Bun Modern Shell. Not completed yet
24
-
25
- See [ARCHITECTURE.md](ARCHITECTURE.md) for the complete architecture, portability model,
26
- and self-bootstrapping design.
30
+ * [jsmdcui](https://github.com/jjtseng93/jsmdcui): Both a text editor and
31
+ Markdown execution runtime (not static rendering), based on
32
+ [bunmicro](https://github.com/jjtseng93/bunmicro)
33
+ * [bunmsh](https://github.com/jjtseng93/bunmsh): Bun Modern Shell โ€” a
34
+ dependency-free, mksh-inspired command shell with a JavaScript mode, and
35
+ builtins that answer the same way on Windows as on POSIX systems
36
+
37
+ - [Why Buninu exists](#why)
38
+ - [Table of contents](#contents)
39
+ - [ARCHITECTURE.md](ARCHITECTURE.md): the complete architecture, portability
40
+ model, and self-bootstrapping design
41
+ - [Commands inside the shell](#commands-inside-the-shell): the reference for a
42
+ session you are already in
27
43
 
28
44
  ## Install Bun
29
45
 
@@ -95,6 +111,14 @@ Drops you straight into [bunmsh](apps/bunmsh/README.md) (Bun Modern Shell), a
95
111
  dependency-free, mksh-inspired command shell that runs on Bun, without going
96
112
  through the browser/jsgotty flow.
97
113
 
114
+ It is marked experimental for one reason above the rest: bunmsh has no job
115
+ control yet. There is no `jobs`, `bg` or `fg`, and no suspending a command
116
+ that is already running โ€” a foreground command holds the terminal until it
117
+ finishes on its own. Everything else a session needs is there: pipelines and
118
+ redirection, functions and compound commands, history, completion with ghost
119
+ suggestions, and a JavaScript mode. Reach for the browser terminal when you
120
+ need to park a job and come back to it.
121
+
98
122
  To try it:
99
123
 
100
124
  ```sh
@@ -124,8 +148,8 @@ Or run it from a source checkout:
124
148
  bun ./bin/init.js --local
125
149
  ```
126
150
 
127
- Buninu's own tools (`rz`, `sz`, `showimg`, `tts`, `xdg-open`, `native-bridge`,
128
- etc.) stay available inside this shell too, same as in the browser session.
151
+ Buninu's own tools (`glow`, `jmi`, `xclip`, `tts`, `xdg-open`, etc.) stay
152
+ available inside this shell too, same as in the browser session.
129
153
 
130
154
  Unlike the shells the browser terminal starts, **bunmsh does not read
131
155
  `.bashrc`**. It does not claim complete POSIX behavior yet, so a startup file
@@ -147,6 +171,407 @@ in a subshell before starting a fresh shell โ€” so aliases and functions a
147
171
  startup command defines are gone by the time you reach a prompt either way.
148
172
  Until bunmsh reads a startup file of its own, sourcing it by hand is the way.
149
173
 
174
+ ## Security
175
+
176
+ **Buninu (when starting a remote shell) binds its terminal server to `127.0.0.1`**, so out of the box it
177
+ only accepts connections from the machine it runs on. It is not reachable from
178
+ other devices on the network unless you explicitly opt in.
179
+
180
+ Within that machine, the terminal is writable (`-w`) and unauthenticated by
181
+ default: only the loopback binding and jsgotty's random URL path (`-r`) stand
182
+ between a local process and a shell with Buninu's permissions.
183
+
184
+ To listen on another interface, pass `--address <value>` (see
185
+ [Start](#start)). Arguments you give on the command line override the defaults
186
+ the package ships in its `scripts.start` entry, the loopback binding included,
187
+ so **pass `--credential user:pass` at the same time** โ€” a random port and URL
188
+ path are not a substitute for authentication once the server is reachable from
189
+ outside the machine.
190
+
191
+ ## Command-line usage
192
+
193
+ These are flags to `bin/init.js` itself, resolved before Buninu starts.
194
+ Everything else on the command line is forwarded to jsgotty.
195
+
196
+ ```text
197
+ Init options:
198
+ --local
199
+ Start Buninu in this terminal
200
+ instead of a Remote Shell
201
+ reached from a browser
202
+ (via jsgotty, the default)
203
+
204
+ -h, --help
205
+ Show this help and exit
206
+ -V, --version
207
+ Show version & runtime info, then exit
208
+
209
+ --readme
210
+ Render README.md in the terminal and exit
211
+ --changelog
212
+ Render CHANGELOG.md in the terminal and exit
213
+
214
+ --readme-tui
215
+ Open README.md as a Terminal UI
216
+ --readme-wui
217
+ Serve README.md as a Web UI
218
+
219
+ --export [output.tgz]
220
+ Export this Buninu installation
221
+ (default: ./buninu.tgz)
222
+
223
+ --export-config [output.json]
224
+ Export Buninu's package.json
225
+ (default: ./buninu.json)
226
+
227
+ Install options:
228
+ -i, --install [dir]
229
+ Install this package into <dir>/buninu
230
+ (default: .)
231
+
232
+ -si, --strip-install [dir]
233
+ Install into <dir> directly
234
+ Without a top-level directory of its own
235
+
236
+ (--install --help for full options)
237
+
238
+ Remote Shell options:
239
+ --shell <path|name>
240
+ Override buninu.shell for this run
241
+ --command <command>
242
+ Override buninu.command for this run
243
+ ```
244
+
245
+ `--local` is the one flag here that changes what Buninu starts rather than
246
+ what it reports; [Start a local shell in a
247
+ Terminal](#start-a-local-shell-in-a-terminal-experimental) covers what that
248
+ session can and cannot do, and [Start](#start) covers the browser route it
249
+ replaces.
250
+
251
+ `--readme-tui` and `--readme-wui` hand README.md to jsmdcui, so its headings
252
+ and its table of contents become links you can follow rather than text you
253
+ scroll past โ€” the first in this terminal, the second at a URL it prints for a
254
+ browser. Opening a Markdown file that way makes jsmdcui write five generated
255
+ files beside it, so Buninu copies README.md into a directory under `TMPDIR`
256
+ first and runs it there: an installation stays yours, and nothing generated
257
+ ends up in an `--export` or outliving an update.
258
+
259
+ `--export` and `--export-config` are described under [Export](#export), which
260
+ also covers what an archive made through `npx` contains and what one made from
261
+ your own installation does instead.
262
+
263
+ `--install --help` lists the install options in full, including `--force` and
264
+ `--yes`; see [Install and update](#install-and-update) for what an update does
265
+ with files you changed.
266
+
267
+ `--shell` and `--command` override for one run what
268
+ [Shell selection](#shell-selection-optional) and
269
+ [Startup command](#startup-command-optional) set in `package.json`.
270
+
271
+ ### Launching a bundled app directly
272
+
273
+ As the **first** argument, `--jsgotty`, `--jsmdcui`, `--bunmsh`, or `--musl-la`
274
+ bypasses the shell and startup-command flow entirely: it spawns that app with
275
+ every remaining argument forwarded to it, and exits with its exit code.
276
+
277
+ ```text
278
+ --jsgotty [args...]
279
+ Spawn jsgotty directly,
280
+ forwarding remaining arguments,
281
+ and exit with its exit code
282
+ (--jsgotty --help = jsgotty options)
283
+
284
+ --jsmdcui [args...]
285
+ Spawn jsmdcui directly,
286
+ same forwarding behavior
287
+
288
+ --bunmsh [args...]
289
+ Spawn bunmsh directly,
290
+ same forwarding behavior
291
+
292
+ --musl-la [args...]
293
+ Spawn musl-la directly,
294
+ same forwarding behavior
295
+ ```
296
+
297
+ Use it to reach an app's own options, which Buninu would otherwise interpret
298
+ as its own โ€” `npx buninu --jsgotty --help` shows jsgotty's flag reference
299
+ rather than this one.
300
+
301
+ ## Commands inside the shell
302
+
303
+ Once you are inside a running Buninu shell, these are available (the
304
+ validated source list is `apps/cmdlist`; see [Add a command](#add-a-command)
305
+ for how it works):
306
+
307
+ ---
308
+
309
+ - **Bun Modern Shell & its builtins**
310
+ * `bunmsh` โ€” Bun Modern Shell supports multi-tabs cwd
311
+ * `catfancy` โ€” Pretty print a file with JSON, YAML, TOML, Markdown and JS/TS colored
312
+ * `lsfancy` โ€” List a directory with emoji, aware of the terminal width
313
+ * `serve` โ€” Serve a directory over HTTP
314
+ * `curl` โ€” Fetch an HTTP URL or download it to a file, built on Bun's `fetch`
315
+ * `pspa` / `pspac` โ€” List processes, plain or colored as shell syntax
316
+ * `kill` โ€” Signal a process, on Windows as well as POSIX
317
+
318
+ Everything below `bunmsh` is one of its builtins, so they are there once you
319
+ are inside a bunmsh session, and every one of them documents itself with
320
+ `--help`. From any other shell, reach them with
321
+ `bunmsh -cc builtin <name> argv1 argv2 ...`.
322
+
323
+ ---
324
+
325
+ - **Markdown applications**
326
+ * `jsmdcui` โ€” Run interactive Markdown applications in both TUI & WebUI
327
+ * `jsmdcui --demo-reader` โ€” Text-to-speech ebook reader
328
+ * `jsmdcui --demo-imgtool` โ€” Image processor with table-based UI based on `Bun.Image`
329
+ * `jsmdcui --demo-imgtool-zh` โ€” The same processor in Traditional Chinese
330
+ * `jsmdcui --demo-maze` โ€” Maze game
331
+ * `jsmdcui --cdp-maze` โ€” The same maze game, started with a local Chrome DevTools
332
+ Protocol server and solved by the bundled solver three seconds later
333
+
334
+ ---
335
+
336
+ - **Editing and viewing**
337
+ * `jmi` โ€” Edit files in the js micro editor
338
+ * `glow` โ€” View file contents with syntax highlighting
339
+
340
+ ---
341
+
342
+ - **Terminal and file transfer**
343
+ * `jsgotty` โ€” Run a browser-accessible terminal
344
+ * `showimg` โ€” Show an image in the terminal
345
+ * `rz` โ€” Upload a file over ZMODEM
346
+ * `sz` โ€” Download a file over ZMODEM
347
+
348
+ These three work in the browser route, a minapk WebView included, but not
349
+ necessarily under `--local`: they speak the Kitty graphics protocol and
350
+ ZMODEM, which a plain terminal emulator need not support. They are
351
+ equivalent to `jsgotty --viu`, `--rz` and `--sz`.
352
+
353
+ ---
354
+
355
+ - **System integration**
356
+ * `xclip` โ€” X11-style clipboard tool; `-selection clipboard`/`-clip` bridges
357
+ to the system clipboard
358
+ * `tts` โ€” Speak text and wait for it to finish (`-a` to not wait)
359
+ * `xdg-open` โ€” Open a file or URL with the platform's default handler
360
+ * `native-bridge` โ€” Call the Android host app (toast, clipboard, speak,
361
+ WebViews) over `PKG_BRIDGE_SOCK`
362
+
363
+ ---
364
+
365
+ - **Running programs**
366
+ * `bun` โ€” Run the bundled Bun, falling back to one already on `PATH`
367
+ * `bunx` โ€” Globally install a package with bun, then exec its matching binary
368
+ * `musl-la` โ€” Launch AArch64 ELF programs with the bundled musl loader
369
+
370
+ ---
371
+
372
+ - **Help**
373
+ * `buninu-help` โ€” Render README.md with glow, then show icon.png with `showimg`
374
+
375
+ ### Details for the above commands
376
+
377
+ `rz [target-dir]` (upload into `target-dir`, default: cwd) and `sz <file>
378
+ [more files...]` (download one or more files) are thin `bin/`-only wrappers
379
+ around `apps/jsgotty/rz.js`/`sz.js`, transferring files over ZMODEM through
380
+ the same terminal connection jsgotty already renders in a browser or WebView.
381
+
382
+ ---
383
+
384
+ `xclip [-o] [-selection primary|clipboard] [-clip]` is a small X11-`xclip`-
385
+ compatible clipboard tool. `-selection primary` (the default) never touches
386
+ the system clipboard, matching real X11 semantics. `-selection
387
+ clipboard`/`-clip` reaches the real system clipboard on Android, macOS,
388
+ Windows, and Linux/Wayland. jsmdcui picks this up automatically once it's on
389
+ `PATH`, so its middle-click paste and copy/paste commands just work.
390
+
391
+ ---
392
+
393
+ `tts <text> [-f|--flush] [-a|--async] [--timeout <ms>] [--pitch <n>]
394
+ [--speed <n>]` speaks text and, by default, blocks until it finishes โ€” no
395
+ timeout unless you pass `--timeout`. Works on Android, macOS, Windows, and
396
+ Linux (via espeak-ng/espeak). `--pitch`/`--speed` fall back to
397
+ `$TTS_PITCH`/`$TTS_SPEED` when not given explicitly, so jsmdcui's own
398
+ pitch/speed setting is honored automatically.
399
+
400
+ ---
401
+
402
+ `glow` and `jmi` are the same program under two names: both run the bundled
403
+ jsmdcui, `glow` with its `--cat` mode, which renders a file to stdout and
404
+ exits, and `jmi` with no mode of its own, which opens the editor because this
405
+ jsmdcui is configured editor-first through `MDCUI_DEFAULT_EDIT`. Neither is
406
+ the Go program called glow โ€” `glow --help` and `jmi --help` both print
407
+ jsmdcui's own reference, and jsmdcui's options are the ones that apply.
408
+
409
+ ---
410
+
411
+ `bun` is the installation's own Bun rather than whatever the machine has. On
412
+ Android it runs `androidNativeLibs/libbun.so` when an APK supplies one;
413
+ otherwise it picks the binary for the machine โ€” `bun-la`, `bun-lx`, or
414
+ `bun-wx.exe` โ€” and extracts it from `apps/bun/bunBin.tgz` first whenever that
415
+ archive is newer than the binary already sitting there. Only when none of
416
+ those is available does it fall back to a Bun on `PATH`, skipping its own
417
+ directory on the way so the `bun` symlink beside it cannot call itself. This
418
+ is what lets an installation carry the runtime it needs with it.
419
+
420
+ ---
421
+
422
+ `bunx <package>[@version] [args...]` installs with `bun i -g` and runs the
423
+ matching binary. On Android, the underlying `bun i -g` currently needs
424
+ [oven-sh/bun#39084](https://github.com/oven-sh/bun/pull/39084) merged
425
+ upstream โ€” without it, install is killed by SIGSYS (Android's seccomp policy
426
+ rejects a syscall bin-linking uses), so `bunx` can't install anything there yet.
427
+
428
+ ---
429
+
430
+ `buninu-help` renders README.md with `jmi`'s `--cat` mode โ€” the same rendering
431
+ `glow` gives โ€” and then shows `icon.png` with `showimg`. It is the command the
432
+ default startup greeting points to.
433
+
434
+ Reach that mode as `jmi --cat` or as plain `glow`, not as `jsmdcui --cat`. The
435
+ `jsmdcui` launcher routes any `.md` argument into the Markdown UI, and opening
436
+ a file that way writes five generated files beside it rather than only
437
+ printing it.
438
+
439
+ ---
440
+
441
+ `musl-la [-e] <program> [args...]` runs an AArch64 Linux ELF program through
442
+ the bundled musl loader, which is what lets a binary built against musl run
443
+ inside a Buninu session. It is AArch64-only, as its suffix says โ€” see
444
+ [Platform-specific binaries](#platform-specific-binaries) โ€” and the loader it
445
+ uses is the APK's `libld-musl.so` when one is present, or
446
+ `apps/musl-la/ld-musl-aarch64.so.1` otherwise.
447
+
448
+ The library path is assembled for you. `musl-la` scans its arguments for the
449
+ first file whose leading four bytes are the ELF magic number, and puts that
450
+ file's own directory on the search path along with `apps/musl-la/` and
451
+ anything already in `LD_LIBRARY_PATH` โ€” so a program with its shared objects
452
+ beside it needs no setup. That path normally reaches the loader as
453
+ `--library-path`; pass `-e` as the first argument to export it as
454
+ `LD_LIBRARY_PATH` instead, which is what a program that goes on to exec
455
+ something else of its own needs.
456
+
457
+ ---
458
+
459
+ `xdg-open <file-or-url>` hands a file or URL to whatever the platform treats
460
+ as its default handler: the host app through native-bridge on Android (or
461
+ `termux-open` under plain Termux), `open` on macOS, `start` on Windows, and
462
+ the real system `xdg-open` on Linux.
463
+
464
+ Inside minapk's APK, `MINAPK_WEBVIEW` redirects that: set it to a WebView id
465
+ and a URL is loaded into that WebView and brought to the front (`openWebView`
466
+ then `showWebView`) instead of being handed to the system's default handler.
467
+
468
+ ```sh
469
+ # In the app WebView, on screen
470
+ MINAPK_WEBVIEW=1 xdg-open https://example.com
471
+
472
+ # ...or for the whole session
473
+ export MINAPK_WEBVIEW=1
474
+ ```
475
+
476
+ `0` is the console, so it navigates the terminal page away โ€” the back key
477
+ returns to it and jsgotty reconnects, but it is not usually what you want.
478
+ `-1` is whichever WebView is in front. Only URLs are redirected: a file path
479
+ always goes to the host's own handler, since WebView cannot read a `file://`
480
+ URL under Buninu's home (`setAllowFileAccess` is false from API 30 on) while
481
+ the host serves that same file through its content:// provider. A value that
482
+ is not a plain integer is reported on stderr and ignored rather than guessed
483
+ at, an unset or empty value keeps the default behavior, and a WebView the host
484
+ does not have falls back to the default handler after saying so.
485
+
486
+ ---
487
+
488
+ `native-bridge [func] [args...]` calls into the Android host app that
489
+ [minapk](https://github.com/jjtseng93/minapk) built the running
490
+ APK with, over an abstract-namespace unix socket. A bare `native-bridge`
491
+ lists what the host implements. Every call
492
+ times out after 5 seconds instead of hanging; outside such an APK, every call
493
+ fails with a clear error instead of doing nothing silently. `import { toast,
494
+ clipboardRead, clipboardWrite, speak, ttsStatus, openWebView, evalWebView,
495
+ showWebView, currWebView, call, available } from
496
+ "apps/native-bridge/native-bridge.js"` gives the same functions as a library,
497
+ for use from a `js back` block.
498
+
499
+ `openWebView(id, url)`, `evalWebView(id, js)`, `showWebView(id)` and
500
+ `currWebView()` (short: `openwv`, `evalwv`, `showwv`, `currwv`) drive the host
501
+ app's WebViews. There are exactly two, both
502
+ alive from startup and neither ever created nor closed at runtime: id `0` is
503
+ the console โ€” the jsgotty terminal this shell is rendered in โ€” and id `1` is
504
+ the app WebView, which starts out blank and behind. Anywhere an id is taken,
505
+ `-1` means whichever WebView is in front right now.
506
+
507
+ `openWebView` loads a URL *without* bringing that WebView to the front, so
508
+ loading the app WebView while the user keeps looking at the terminal is one
509
+ call. `showWebView` is the only thing that changes what is on screen; the
510
+ host's on-screen key bar (Ctrl/Alt/Shift and friends), its volume-key menu and
511
+ its back key all act on whichever WebView is in front, so they follow the
512
+ switch. Back on the app WebView with no page of its own to go back to
513
+ switches to the console rather than leaving the app, unless
514
+ `buninu.backToConsole` says otherwise (see "Back key" below).
515
+ `showWebView(-1)` switches to the next WebView instead of being a no-op --
516
+ with two of them, a toggle. `evalWebView` resolves to the value the
517
+ expression produced, not a string containing it; `undefined`, a function, and
518
+ a thrown exception all arrive as `null`, since WebView itself does not
519
+ distinguish them.
520
+
521
+ All four also answer to a short `openwv`/`evalwv`/`showwv`/`currwv` spelling,
522
+ the same way `clipboardRead`/`clipboardWrite` answer to `getcb`/`setcb`: the
523
+ host accepts either and lists both in its `_discover` response, so either name
524
+ works from the CLI, from `rpcraw`, and as an `import`.
525
+
526
+ ```sh
527
+ # Load it, screen unchanged
528
+ native-bridge openwv 1 https://example.com
529
+
530
+ # Now bring it to the front
531
+ native-bridge showwv 1
532
+
533
+ native-bridge evalwv 1 document.title
534
+ native-bridge currwv
535
+ ```
536
+
537
+ ## Differences from upstream
538
+
539
+ The versions of jsmdcui and jsgotty bundled here differ from the ones their
540
+ own projects ship.
541
+
542
+ ### The bundled jsmdcui
543
+
544
+ * Is configured editor-first by `MDCUI_DEFAULT_EDIT`
545
+ * The command `jmi` with a Markdown file opens the normal terminal editor
546
+ (js micro editor)
547
+ * The command `jsmdcui` preserves its original behavior:
548
+ `apps/jsmdcui/jsmdcui.sh`, which starts its `tui` entry point with
549
+ `--mdcui` when running Markdown apps, and forwards all command-line
550
+ arguments
551
+ * Adds the Buninu-only `# syntax: markdown` marker for Markdown highlighting
552
+ in extensionless files; upstreaming may be considered later
553
+
554
+ ### The bundled jsgotty
555
+
556
+ * No longer depends on or ships `node-pty`
557
+ * Its PTY is provided by Bun's terminal API
558
+
559
+ ## Optional external tools
560
+
561
+ `bunproot` is an optional tool downloaded by the user through `bunx` and is
562
+ licensed under GPL-2.0-or-later; `js-udocker` is licensed under Apache-2.0.
563
+ Neither is distributed with Buninu. The example below is Android-only.
564
+
565
+ ```sh
566
+ bunx bunproot
567
+ bunx bunproot --git clone https://github.com/jjtseng93/bunproot
568
+ bunx bunproot --git clone https://github.com/jjtseng93/js-udocker
569
+ cd js-udocker
570
+ export JS_UDOCKER_BUNPROOT=$(realpath ../bunproot/proot.js)
571
+ bun udocker.js run --name=ap alpine
572
+ # bun udocker.js ps
573
+ ```
574
+
150
575
  ## Data & Persistence
151
576
 
152
577
  Running Buninu via `npx` works like a container: `npx` fetches the package into
@@ -280,209 +705,6 @@ A source checkout's own `.git` is never copied into an installation, so
280
705
  installing into a directory that is itself a repository leaves that repository
281
706
  alone.
282
707
 
283
- ## Security
284
-
285
- `scripts.start` binds jsgotty to `127.0.0.1` by default, so the terminal
286
- server only accepts connections from the same machine. It is not reachable
287
- from other devices on the network unless you explicitly opt in.
288
-
289
- The terminal itself is writable (`-w`) and unauthenticated by default; only
290
- the loopback binding and jsgotty's random URL path (`-r`) stand between a
291
- local process and a shell with Buninu's permissions. To listen on another
292
- interface, forward `--address <value>` (see [Start](#start)); forwarded
293
- arguments override the flags baked into `scripts.start`. Also pass
294
- `--credential user:pass` when doing so, since the random port and URL path
295
- are not a substitute for authentication once the server is reachable from
296
- outside the machine.
297
-
298
- ## Command-line usage
299
-
300
- These are flags to `bin/init.js` itself, resolved before Buninu starts.
301
- Everything else on the command line is forwarded to jsgotty.
302
-
303
- ```text
304
- -h, --help Show command-line help
305
- -V, --version Show the Buninu, Bun, platform, and architecture versions
306
- --readme Render README.md in the terminal
307
- --changelog Render CHANGELOG.md in the terminal
308
- --local Start bunmsh in this terminal instead of a browser terminal
309
-
310
- -i, --install [dir] Install into <dir>/buninu (default: .)
311
- -si, --strip-install [dir] Install into <dir> itself
312
- --export [output.tgz] Export this installation as a tarball
313
- --export-config [out.json] Export this package.json
314
-
315
- --shell <path|name> Override buninu.shell for this run
316
- --command <command> Override buninu.command for this run
317
- ```
318
-
319
- `--install --help` lists the install options in full, including `--force` and
320
- `--yes`; see [Install and update](#install-and-update) for what an update does
321
- with files you changed. The other groups are covered by
322
- [Start](#start), [Export](#export), [Shell selection](#shell-selection-optional)
323
- and [Startup command](#startup-command-optional).
324
-
325
- ### Launching a bundled app directly
326
-
327
- As the **first** argument, `--jsgotty`, `--jsmdcui`, or `--musl-la` bypasses the
328
- shell and startup-command flow entirely: it spawns that app with every
329
- remaining argument forwarded to it, and exits with its exit code.
330
-
331
- ```text
332
- --jsgotty [args...] Spawn jsgotty directly and exit with its exit code
333
- --jsmdcui [args...] Spawn jsmdcui directly and exit with its exit code
334
- --musl-la [args...] Spawn musl-la directly and exit with its exit code
335
- ```
336
-
337
- Use it to reach an app's own options, which Buninu would otherwise interpret
338
- as its own โ€” `npx buninu --jsgotty --help` shows jsgotty's flag reference
339
- rather than this one.
340
-
341
- ## Commands inside the shell
342
-
343
- Once you are inside a running Buninu shell, these are available (the
344
- validated source list is `apps/cmdlist`; see [Add a command](#add-a-command)
345
- for how it works):
346
-
347
- ```text
348
- glow Render files with jsmdcui syntax highlighting
349
- jmi Open files in the js micro editor
350
- jsgotty Run a browser-accessible terminal
351
- jsmdcui Edit and run interactive Markdown applications
352
- musl-la Launch AArch64 ELF programs with the bundled musl loader
353
- buninu-help Render README.md with glow, then show icon.png with jsgotty --viu
354
- bunx Globally install a package with bun, then exec its matching binary
355
- native-bridge Call the Android host app (toast, clipboard, speak, WebViews) over PKG_BRIDGE_SOCK
356
- xclip X11-style clipboard tool; -selection clipboard/-clip bridges to the system clipboard
357
- tts Speak text and wait for it to finish (-a to not wait)
358
- showimg Shorthand for jsgotty --viu
359
- rz Upload a file over ZMODEM
360
- sz Download a file over ZMODEM
361
- xdg-open Open a file or URL with the platform's default handler
362
- ```
363
-
364
- `buninu-help` renders README.md with jsmdcui's `--cat` mode and then shows
365
- `icon.png` with jsgotty's `--viu`. It is the command the default startup
366
- greeting points to.
367
-
368
- `bunx <package>[@version] [args...]` installs with `bun i -g` and runs the
369
- matching binary. On Android, the underlying `bun i -g` currently needs
370
- [oven-sh/bun#39084](https://github.com/oven-sh/bun/pull/39084) merged
371
- upstream โ€” without it, install is killed by SIGSYS (Android's seccomp policy
372
- rejects a syscall bin-linking uses), so `bunx` can't install anything there yet.
373
-
374
- `native-bridge [func] [args...]` calls into the Android host app that
375
- [minapk](https://www.npmjs.com/package/@drxiaozhi/minapk) built the running
376
- APK with, over an abstract-namespace unix socket. A bare `native-bridge`
377
- lists what the host implements. Every call
378
- times out after 5 seconds instead of hanging; outside such an APK, every call
379
- fails with a clear error instead of doing nothing silently. `import { toast,
380
- clipboardRead, clipboardWrite, speak, ttsStatus, openWebView, evalWebView,
381
- showWebView, currWebView, call, available } from
382
- "apps/native-bridge/native-bridge.js"` gives the same functions as a library,
383
- for use from a `js back` block.
384
-
385
- `openWebView(id, url)`, `evalWebView(id, js)`, `showWebView(id)` and
386
- `currWebView()` (short: `openwv`, `evalwv`, `showwv`, `currwv`) drive the host
387
- app's WebViews. There are exactly two, both
388
- alive from startup and neither ever created nor closed at runtime: id `0` is
389
- the console -- the jsgotty terminal this shell is rendered in -- and id `1` is
390
- the app WebView, which starts out blank and behind. Anywhere an id is taken,
391
- `-1` means whichever WebView is in front right now.
392
-
393
- `openWebView` loads a URL *without* bringing that WebView to the front, so
394
- loading the app WebView while the user keeps looking at the terminal is one
395
- call. `showWebView` is the only thing that changes what is on screen; the
396
- host's on-screen key bar (Ctrl/Alt/Shift and friends), its volume-key menu and
397
- its back key all act on whichever WebView is in front, so they follow the
398
- switch. Back on the app WebView with no page of its own to go back to
399
- switches to the console rather than leaving the app, unless
400
- `buninu.backToConsole` says otherwise (see "Back key" below).
401
- `showWebView(-1)` switches to the next WebView instead of being a no-op --
402
- with two of them, a toggle. `evalWebView` resolves to the value the
403
- expression produced, not a string containing it; `undefined`, a function, and
404
- a thrown exception all arrive as `null`, since WebView itself does not
405
- distinguish them.
406
-
407
- All four also answer to a short `openwv`/`evalwv`/`showwv`/`currwv` spelling,
408
- the same way `clipboardRead`/`clipboardWrite` answer to `getcb`/`setcb`: the
409
- host accepts either and lists both in its `_discover` response, so either name
410
- works from the CLI, from `rpcraw`, and as an `import`.
411
-
412
- ```sh
413
- # Load it, screen unchanged
414
- native-bridge openwv 1 https://example.com
415
-
416
- # Now bring it to the front
417
- native-bridge showwv 1
418
-
419
- native-bridge evalwv 1 document.title
420
- native-bridge currwv
421
- ```
422
-
423
- `xclip [-o] [-selection primary|clipboard] [-clip]` is a small X11-`xclip`-
424
- compatible clipboard tool. `-selection primary` (the default) never touches
425
- the system clipboard, matching real X11 semantics. `-selection
426
- clipboard`/`-clip` reaches the real system clipboard on Android, macOS,
427
- Windows, and Linux/Wayland. jsmdcui picks this up automatically once it's on
428
- `PATH`, so its middle-click paste and copy/paste commands just work.
429
-
430
- `tts <text> [-f|--flush] [-a|--async] [--timeout <ms>] [--pitch <n>]
431
- [--speed <n>]` speaks text and, by default, blocks until it finishes -- no
432
- timeout unless you pass `--timeout`. Works on Android, macOS, Windows, and
433
- Linux (via espeak-ng/espeak). `--pitch`/`--speed` fall back to
434
- `$TTS_PITCH`/`$TTS_SPEED` when not given explicitly, so jsmdcui's own
435
- pitch/speed setting is honored automatically.
436
-
437
- `rz [target-dir]` (upload into `target-dir`, default: cwd) and `sz <file>
438
- [more files...]` (download one or more files) are thin `bin/`-only wrappers
439
- around `apps/jsgotty/rz.js`/`sz.js`, transferring files over ZMODEM through
440
- the same terminal connection jsgotty already renders in a browser or WebView.
441
-
442
- `xdg-open <file-or-url>` hands a file or URL to whatever the platform treats
443
- as its default handler: the host app through native-bridge on Android (or
444
- `termux-open` under plain Termux), `open` on macOS, `start` on Windows, and
445
- the real system `xdg-open` on Linux.
446
-
447
- Inside minapk's APK, `MINAPK_WEBVIEW` redirects that: set it to a WebView id
448
- and a URL is loaded into that WebView and brought to the front (`openWebView`
449
- then `showWebView`) instead of being handed to the system's default handler.
450
-
451
- ```sh
452
- # In the app WebView, on screen
453
- MINAPK_WEBVIEW=1 xdg-open https://example.com
454
-
455
- # ...or for the whole session
456
- export MINAPK_WEBVIEW=1
457
- ```
458
-
459
- `0` is the console, so it navigates the terminal page away -- the back key
460
- returns to it and jsgotty reconnects, but it is not usually what you want.
461
- `-1` is whichever WebView is in front. Only URLs are redirected: a file path
462
- always goes to the host's own handler, since WebView cannot read a `file://`
463
- URL under Buninu's home (`setAllowFileAccess` is false from API 30 on) while
464
- the host serves that same file through its content:// provider. A value that
465
- is not a plain integer is reported on stderr and ignored rather than guessed
466
- at, an unset or empty value keeps the default behavior, and a WebView the host
467
- does not have falls back to the default handler after saying so.
468
-
469
- ## Differences from upstream
470
-
471
- The versions of jsmdcui and jsgotty bundled here differ from the ones their
472
- own projects ship.
473
-
474
- ### The bundled jsmdcui
475
-
476
- * Is configured editor-first by MDCUI_DEFAULT_EDIT
477
- * The command `jmi` with a Markdown file opens the normal terminal editor.(js micro editor)
478
- * The command `jsmdcui` preserves its original behavior: `apps/jsmdcui/jsmdcui.sh`, which starts its `tui` entry point with `--mdcui` when running markdown Apps, and forwards all command-line arguments.
479
- * Adds the Buninu-only `# syntax: markdown` marker for Markdown highlighting in extensionless files; upstreaming may be considered later.
480
-
481
- ### The bundled jsgotty
482
-
483
- * No longer depends on or ships `node-pty`.
484
- * Its PTY is provided by Bun's terminal API.
485
-
486
708
  ## Export
487
709
 
488
710
  Export a Buninu installation as a gzip-compressed tar archive. What gets
@@ -623,6 +845,29 @@ call "%~dp0bun.bat" "%~dp0..\apps\hello\hello.js" %*
623
845
  exit /b %ERRORLEVEL%
624
846
  ```
625
847
 
848
+ ### Platform-specific binaries
849
+
850
+ A bundled binary that only runs on one platform and architecture carries a
851
+ two-letter suffix: the platform first, then the architecture.
852
+
853
+ | Suffix | Platform | Architecture |
854
+ |---|---|---|
855
+ | `la` | Linux | arm64 |
856
+ | `lx` | Linux | x64 |
857
+ | `aa` | Android | arm64 |
858
+ | `wx` | Windows | x64 |
859
+ | `ma` | macOS | arm64 |
860
+
861
+ So `bin/bun.sh` picks `bun-la`, `bun-lx` or `bun-wx.exe` for the machine it
862
+ finds itself on, and `musl-la` is the Linux/arm64 ELF loader โ€” on any other
863
+ platform there is nothing for it to load. A name without a suffix is
864
+ portable: `hello.js` above runs wherever Bun does.
865
+
866
+ One binary is deliberately outside the scheme. `bin/libsh-loader.so` is
867
+ Android/arm64 and would otherwise be `-aa`, but Android extracts and executes
868
+ only files named `lib*.so` from an APK's native library directory, so that
869
+ name belongs to the platform rather than to this convention.
870
+
626
871
  ## Startup command (optional)
627
872
 
628
873
  Set `buninu.command.default` or a platform-specific value (`android`, `linux`,
@@ -749,7 +994,7 @@ and running behind it. Set it to `false` and back leaves the app instead.
749
994
 
750
995
  This one is read by the host app, not by Buninu itself, so it does nothing
751
996
  outside an APK built with
752
- [minapk](https://www.npmjs.com/package/@drxiaozhi/minapk) -- where it can also
997
+ [minapk](https://github.com/jjtseng93/minapk) โ€” where it can also
753
998
  be set for a single build with `--no-back-to-console`. The host treats a
754
999
  missing, unreadable, or non-boolean value as `true`, so nothing here can fail
755
1000
  in a way that leaves the back key broken.
@@ -813,3 +1058,96 @@ words, and `pspac` is two commands joined by `;` with a redirection in the
813
1058
  first, so it cannot be one โ€” but the builtin of that name is already there, and
814
1059
  an alias that fails to define leaves it reachable. See
815
1060
  [Start a local shell in a Terminal](#start-a-local-shell-in-a-terminal-experimental).
1061
+
1062
+ ## Why
1063
+
1064
+ - The usual way to get a Unix
1065
+ environment onto a machine
1066
+ * A system package manager
1067
+ * A compiler
1068
+ * On Android or Windows,
1069
+ you install Linux first:
1070
+ WSL, or a proot distro
1071
+
1072
+ - Buninu's way: everything it
1073
+ needs is JavaScript on Bun
1074
+ * Nothing to build
1075
+ * Nothing installed
1076
+ system-wide
1077
+ * The bundled jsgotty
1078
+ dropped `node-pty` for
1079
+ Bun's own terminal API
1080
+ * So the same tree runs
1081
+ unchanged on a phone, a
1082
+ laptop and a server
1083
+
1084
+ - Not a bare runtime either
1085
+ * An editor and Markdown
1086
+ runtime
1087
+ * A clipboard tool
1088
+ * Text-to-speech
1089
+ * ZMODEM file transfer
1090
+ * An `xdg-open` that knows
1091
+ each platform's real
1092
+ handler
1093
+
1094
+ - An installation is one
1095
+ directory
1096
+ * Yours to edit
1097
+ * Copy it to another machine
1098
+ and your shell, the
1099
+ commands you added and
1100
+ your history go with it
1101
+ * Your own `$HOME` is never
1102
+ touched โ€” a shell started
1103
+ here still finds your SSH
1104
+ keys and Git configuration
1105
+
1106
+ ## Contents
1107
+
1108
+ - [Install Bun](#install-bun)
1109
+ - [Start](#start)
1110
+ * [Start a remote shell in a Browser](#start-a-remote-shell-in-a-browser)
1111
+ * [Start a local shell in a Terminal (experimental)](#start-a-local-shell-in-a-terminal-experimental)
1112
+ - [Security](#security)
1113
+ - [Command-line usage](#command-line-usage)
1114
+ * [Launching a bundled app directly](#launching-a-bundled-app-directly)
1115
+ - [Commands inside the shell](#commands-inside-the-shell)
1116
+ - [Differences from upstream](#differences-from-upstream)
1117
+ - [Optional external tools](#optional-external-tools)
1118
+ - [Data & Persistence](#data--persistence)
1119
+ - [Install and update](#install-and-update)
1120
+ - [Export](#export)
1121
+ - [Environment](#environment)
1122
+ - [Add a command](#add-a-command)
1123
+ * [Platform-specific binaries](#platform-specific-binaries)
1124
+ - [Startup command (optional)](#startup-command-optional)
1125
+ - [Shell selection (optional)](#shell-selection-optional)
1126
+ - [Data directory (optional)](#data-directory-optional)
1127
+ - [Back key (Android, optional)](#back-key-android-optional)
1128
+ - [Process-list helpers](#process-list-helpers)
1129
+ - [Why](#why)
1130
+ - [License](#license)
1131
+
1132
+ ## License
1133
+
1134
+ Buninu's own code is MIT โ€” see [LICENSE](LICENSE).
1135
+
1136
+ The apps bundled under `apps/` are separate projects on their own terms, and
1137
+ each carries its licence with it:
1138
+
1139
+ | App | Licence | Files under `apps/<app>/` |
1140
+ |---|---|---|
1141
+ | jsgotty | MIT; its bundled front-end libraries are MIT and Apache-2.0 | `LICENSE`, `NOTICE`, `LICENSES/`, `static/js/gotty.licenses.txt` |
1142
+ | jsmdcui | MIT | `LICENSE`, `runtime/syntax/LICENSE` |
1143
+ | bunmsh | MIT; mksh's own terms are not relicensed under it | `LICENSE`, `LICENSE-MKSH`, `LICENSE-MICRO` |
1144
+ | musl-la | MIT for musl itself, and see below | `LICENSE_musl.txt`, `NOTICE`, `LICENSES/` |
1145
+
1146
+ One component is worth naming here rather than leaving in a file to be found:
1147
+ `apps/musl-la` ships `libgcc_s.so.1` and `libstdc++.so.6`, built from GCC
1148
+ 14.2.0, under **GPL-3.0 with the GCC Runtime Library Exception**. That
1149
+ exception is what lets them be distributed alongside code under any licence,
1150
+ so nothing here changes Buninu's own MIT terms โ€” but if your organisation
1151
+ screens for GPL, this is the component it will find. `apps/musl-la/NOTICE`
1152
+ records the exact build, its Corresponding Source, and SHA-256 sums for every
1153
+ binary in that directory.