buninu 0.4.6 โ†’ 0.4.7

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,391 @@ 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
+
150
559
  ## Data & Persistence
151
560
 
152
561
  Running Buninu via `npx` works like a container: `npx` fetches the package into
@@ -280,209 +689,6 @@ A source checkout's own `.git` is never copied into an installation, so
280
689
  installing into a directory that is itself a repository leaves that repository
281
690
  alone.
282
691
 
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
692
  ## Export
487
693
 
488
694
  Export a Buninu installation as a gzip-compressed tar archive. What gets
@@ -623,6 +829,29 @@ call "%~dp0bun.bat" "%~dp0..\apps\hello\hello.js" %*
623
829
  exit /b %ERRORLEVEL%
624
830
  ```
625
831
 
832
+ ### Platform-specific binaries
833
+
834
+ A bundled binary that only runs on one platform and architecture carries a
835
+ two-letter suffix: the platform first, then the architecture.
836
+
837
+ | Suffix | Platform | Architecture |
838
+ |---|---|---|
839
+ | `la` | Linux | arm64 |
840
+ | `lx` | Linux | x64 |
841
+ | `aa` | Android | arm64 |
842
+ | `wx` | Windows | x64 |
843
+ | `ma` | macOS | arm64 |
844
+
845
+ So `bin/bun.sh` picks `bun-la`, `bun-lx` or `bun-wx.exe` for the machine it
846
+ finds itself on, and `musl-la` is the Linux/arm64 ELF loader โ€” on any other
847
+ platform there is nothing for it to load. A name without a suffix is
848
+ portable: `hello.js` above runs wherever Bun does.
849
+
850
+ One binary is deliberately outside the scheme. `bin/libsh-loader.so` is
851
+ Android/arm64 and would otherwise be `-aa`, but Android extracts and executes
852
+ only files named `lib*.so` from an APK's native library directory, so that
853
+ name belongs to the platform rather than to this convention.
854
+
626
855
  ## Startup command (optional)
627
856
 
628
857
  Set `buninu.command.default` or a platform-specific value (`android`, `linux`,
@@ -749,7 +978,7 @@ and running behind it. Set it to `false` and back leaves the app instead.
749
978
 
750
979
  This one is read by the host app, not by Buninu itself, so it does nothing
751
980
  outside an APK built with
752
- [minapk](https://www.npmjs.com/package/@drxiaozhi/minapk) -- where it can also
981
+ [minapk](https://github.com/jjtseng93/minapk) โ€” where it can also
753
982
  be set for a single build with `--no-back-to-console`. The host treats a
754
983
  missing, unreadable, or non-boolean value as `true`, so nothing here can fail
755
984
  in a way that leaves the back key broken.
@@ -813,3 +1042,95 @@ words, and `pspac` is two commands joined by `;` with a redirection in the
813
1042
  first, so it cannot be one โ€” but the builtin of that name is already there, and
814
1043
  an alias that fails to define leaves it reachable. See
815
1044
  [Start a local shell in a Terminal](#start-a-local-shell-in-a-terminal-experimental).
1045
+
1046
+ ## Why
1047
+
1048
+ - The usual way to get a Unix
1049
+ environment onto a machine
1050
+ * A system package manager
1051
+ * A compiler
1052
+ * On Android or Windows,
1053
+ you install Linux first:
1054
+ WSL, or a proot distro
1055
+
1056
+ - Buninu's way: everything it
1057
+ needs is JavaScript on Bun
1058
+ * Nothing to build
1059
+ * Nothing installed
1060
+ system-wide
1061
+ * The bundled jsgotty
1062
+ dropped `node-pty` for
1063
+ Bun's own terminal API
1064
+ * So the same tree runs
1065
+ unchanged on a phone, a
1066
+ laptop and a server
1067
+
1068
+ - Not a bare runtime either
1069
+ * An editor and Markdown
1070
+ runtime
1071
+ * A clipboard tool
1072
+ * Text-to-speech
1073
+ * ZMODEM file transfer
1074
+ * An `xdg-open` that knows
1075
+ each platform's real
1076
+ handler
1077
+
1078
+ - An installation is one
1079
+ directory
1080
+ * Yours to edit
1081
+ * Copy it to another machine
1082
+ and your shell, the
1083
+ commands you added and
1084
+ your history go with it
1085
+ * Your own `$HOME` is never
1086
+ touched โ€” a shell started
1087
+ here still finds your SSH
1088
+ keys and Git configuration
1089
+
1090
+ ## Contents
1091
+
1092
+ - [Install Bun](#install-bun)
1093
+ - [Start](#start)
1094
+ * [Start a remote shell in a Browser](#start-a-remote-shell-in-a-browser)
1095
+ * [Start a local shell in a Terminal (experimental)](#start-a-local-shell-in-a-terminal-experimental)
1096
+ - [Security](#security)
1097
+ - [Command-line usage](#command-line-usage)
1098
+ * [Launching a bundled app directly](#launching-a-bundled-app-directly)
1099
+ - [Commands inside the shell](#commands-inside-the-shell)
1100
+ - [Differences from upstream](#differences-from-upstream)
1101
+ - [Data & Persistence](#data--persistence)
1102
+ - [Install and update](#install-and-update)
1103
+ - [Export](#export)
1104
+ - [Environment](#environment)
1105
+ - [Add a command](#add-a-command)
1106
+ * [Platform-specific binaries](#platform-specific-binaries)
1107
+ - [Startup command (optional)](#startup-command-optional)
1108
+ - [Shell selection (optional)](#shell-selection-optional)
1109
+ - [Data directory (optional)](#data-directory-optional)
1110
+ - [Back key (Android, optional)](#back-key-android-optional)
1111
+ - [Process-list helpers](#process-list-helpers)
1112
+ - [Why](#why)
1113
+ - [License](#license)
1114
+
1115
+ ## License
1116
+
1117
+ Buninu's own code is MIT โ€” see [LICENSE](LICENSE).
1118
+
1119
+ The apps bundled under `apps/` are separate projects on their own terms, and
1120
+ each carries its licence with it:
1121
+
1122
+ | App | Licence | Files under `apps/<app>/` |
1123
+ |---|---|---|
1124
+ | jsgotty | MIT; its bundled front-end libraries are MIT and Apache-2.0 | `LICENSE`, `NOTICE`, `LICENSES/`, `static/js/gotty.licenses.txt` |
1125
+ | jsmdcui | MIT | `LICENSE`, `runtime/syntax/LICENSE` |
1126
+ | bunmsh | MIT; mksh's own terms are not relicensed under it | `LICENSE`, `LICENSE-MKSH`, `LICENSE-MICRO` |
1127
+ | musl-la | MIT for musl itself, and see below | `LICENSE_musl.txt`, `NOTICE`, `LICENSES/` |
1128
+
1129
+ One component is worth naming here rather than leaving in a file to be found:
1130
+ `apps/musl-la` ships `libgcc_s.so.1` and `libstdc++.so.6`, built from GCC
1131
+ 14.2.0, under **GPL-3.0 with the GCC Runtime Library Exception**. That
1132
+ exception is what lets them be distributed alongside code under any licence,
1133
+ so nothing here changes Buninu's own MIT terms โ€” but if your organisation
1134
+ screens for GPL, this is the component it will find. `apps/musl-la/NOTICE`
1135
+ records the exact build, its Corresponding Source, and SHA-256 sums for every
1136
+ binary in that directory.