buninu 0.2.4 → 0.3.0
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/CHANGELOG.md +45 -0
- package/README.md +79 -2
- package/apps/native-bridge/native-bridge.js +45 -0
- package/apps/xdg-open/xdg-open.js +74 -2
- package/bin/init.js +7 -6
- package/demo-server.js +64 -0
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0 - 2026-08-21
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Add `openWebView`, `evalWebView`, `showWebView` and `currWebView` to
|
|
8
|
+
`apps/native-bridge` (short spellings `openwv`, `evalwv`, `showwv`,
|
|
9
|
+
`currwv`, both accepted over the wire and listed in the host's `_discover`,
|
|
10
|
+
the same arrangement `getcb`/`setcb` already had), plus the
|
|
11
|
+
`WEBVIEW_CURRENT`/`WEBVIEW_CONSOLE`/`WEBVIEW_APP` constants. The host app
|
|
12
|
+
has exactly two WebViews, alive from startup and never created or closed at
|
|
13
|
+
runtime: `0` is the console showing the jsgotty terminal, `1` is the app
|
|
14
|
+
WebView, which starts blank and behind. An id of `-1` means whichever is in
|
|
15
|
+
front. `openWebView` loads without bringing its target forward, so loading
|
|
16
|
+
the app WebView while the user keeps looking at the terminal is one call;
|
|
17
|
+
`showWebView` is the only thing that changes what is on screen, and
|
|
18
|
+
`showWebView(-1)` switches to the *next* WebView rather than being a no-op.
|
|
19
|
+
`evalWebView` resolves to the value the expression produced rather than a
|
|
20
|
+
string containing it, with `undefined`, functions and thrown exceptions all
|
|
21
|
+
arriving as `null` because WebView itself does not distinguish them.
|
|
22
|
+
- `xdg-open` gained `MINAPK_WEBVIEW`, which names the WebView to open a URL
|
|
23
|
+
in (and brings it to the front) instead of handing the URL to the
|
|
24
|
+
platform's default handler. Read here rather than by the host app on
|
|
25
|
+
purpose: it is an environment variable of this process tree, so the host
|
|
26
|
+
could not see a value exported in the shell a moment ago, which is the
|
|
27
|
+
whole point of having it. Only URLs are redirected -- a filesystem path
|
|
28
|
+
still goes to the host, whose `content://` provider can actually serve it,
|
|
29
|
+
unlike a `file://` URL in a WebView. A value that is not a plain integer is
|
|
30
|
+
reported and ignored rather than guessed at, and a WebView the host does
|
|
31
|
+
not have falls back to the default handler after saying so.
|
|
32
|
+
- Add `demo-server.js`: serves one fixed page on an OS-assigned port and
|
|
33
|
+
opens it in the app WebView through `MINAPK_WEBVIEW=1 xdg-open`, as the
|
|
34
|
+
smallest end-to-end exercise of the above.
|
|
35
|
+
- Add `buninu.backToConsole` (default `true`), read by the minapk host app,
|
|
36
|
+
not by Buninu itself: it decides whether the Android back key, pressed on
|
|
37
|
+
the app WebView with no page of its own to go back to, switches to the
|
|
38
|
+
console WebView or takes the leave-the-app path. The host treats a missing,
|
|
39
|
+
unreadable or non-boolean value as `true`, so it cannot fail in a way that
|
|
40
|
+
leaves the back key broken. minapk's `--no-back-to-console` sets it for a
|
|
41
|
+
single build.
|
|
42
|
+
|
|
43
|
+
## 0.2.5 - 2026-08-19
|
|
44
|
+
|
|
45
|
+
- Fixed fish shell startup breaking buninu
|
|
46
|
+
- Start fish after entering the shell please
|
|
47
|
+
|
|
3
48
|
## 0.2.4 - 2026-08-19
|
|
4
49
|
|
|
5
50
|
### Added
|
package/README.md
CHANGED
|
@@ -125,7 +125,7 @@ jsmdcui Edit and run interactive Markdown applications
|
|
|
125
125
|
musl-la Launch AArch64 ELF programs with the bundled musl loader
|
|
126
126
|
buninu-help Render README.md with glow, then show icon.png with jsgotty --viu
|
|
127
127
|
bunx Globally install a package with bun, then exec its matching binary
|
|
128
|
-
native-bridge Call the Android host app (toast, clipboard, speak) over PKG_BRIDGE_SOCK
|
|
128
|
+
native-bridge Call the Android host app (toast, clipboard, speak, WebViews) over PKG_BRIDGE_SOCK
|
|
129
129
|
xclip X11-style clipboard tool; -selection clipboard/-clip bridges to the system clipboard
|
|
130
130
|
tts Speak text and wait for it to finish (-a to not wait)
|
|
131
131
|
showimg Shorthand for jsgotty --viu
|
|
@@ -150,10 +150,45 @@ APK with, over an abstract-namespace unix socket. A bare `native-bridge`
|
|
|
150
150
|
lists what the host implements. Every call
|
|
151
151
|
times out after 5 seconds instead of hanging; outside such an APK, every call
|
|
152
152
|
fails with a clear error instead of doing nothing silently. `import { toast,
|
|
153
|
-
clipboardRead, clipboardWrite, speak, ttsStatus,
|
|
153
|
+
clipboardRead, clipboardWrite, speak, ttsStatus, openWebView, evalWebView,
|
|
154
|
+
showWebView, currWebView, call, available } from
|
|
154
155
|
"apps/native-bridge/native-bridge.js"` gives the same functions as a library,
|
|
155
156
|
for use from a `js back` block.
|
|
156
157
|
|
|
158
|
+
`openWebView(id, url)`, `evalWebView(id, js)`, `showWebView(id)` and
|
|
159
|
+
`currWebView()` (short: `openwv`, `evalwv`, `showwv`, `currwv`) drive the host
|
|
160
|
+
app's WebViews. There are exactly two, both
|
|
161
|
+
alive from startup and neither ever created nor closed at runtime: id `0` is
|
|
162
|
+
the console -- the jsgotty terminal this shell is rendered in -- and id `1` is
|
|
163
|
+
the app WebView, which starts out blank and behind. Anywhere an id is taken,
|
|
164
|
+
`-1` means whichever WebView is in front right now.
|
|
165
|
+
|
|
166
|
+
`openWebView` loads a URL *without* bringing that WebView to the front, so
|
|
167
|
+
loading the app WebView while the user keeps looking at the terminal is one
|
|
168
|
+
call. `showWebView` is the only thing that changes what is on screen; the
|
|
169
|
+
host's on-screen key bar (Ctrl/Alt/Shift and friends), its volume-key menu and
|
|
170
|
+
its back key all act on whichever WebView is in front, so they follow the
|
|
171
|
+
switch. Back on the app WebView with no page of its own to go back to
|
|
172
|
+
switches to the console rather than leaving the app, unless
|
|
173
|
+
`buninu.backToConsole` says otherwise (see "Back key" below).
|
|
174
|
+
`showWebView(-1)` switches to the next WebView instead of being a no-op --
|
|
175
|
+
with two of them, a toggle. `evalWebView` resolves to the value the
|
|
176
|
+
expression produced, not a string containing it; `undefined`, a function, and
|
|
177
|
+
a thrown exception all arrive as `null`, since WebView itself does not
|
|
178
|
+
distinguish them.
|
|
179
|
+
|
|
180
|
+
All four also answer to a short `openwv`/`evalwv`/`showwv`/`currwv` spelling,
|
|
181
|
+
the same way `clipboardRead`/`clipboardWrite` answer to `getcb`/`setcb`: the
|
|
182
|
+
host accepts either and lists both in its `_discover` response, so either name
|
|
183
|
+
works from the CLI, from `rpcraw`, and as an `import`.
|
|
184
|
+
|
|
185
|
+
```sh
|
|
186
|
+
native-bridge openwv 1 https://example.com # load it, screen unchanged
|
|
187
|
+
native-bridge showwv 1 # now bring it to the front
|
|
188
|
+
native-bridge evalwv 1 document.title
|
|
189
|
+
native-bridge currwv
|
|
190
|
+
```
|
|
191
|
+
|
|
157
192
|
`xclip [-o] [-selection primary|clipboard] [-clip]` is a small X11-`xclip`-
|
|
158
193
|
compatible clipboard tool. `-selection primary` (the default) never touches
|
|
159
194
|
the system clipboard, matching real X11 semantics. `-selection
|
|
@@ -178,6 +213,25 @@ as its default handler: the host app through native-bridge on Android (or
|
|
|
178
213
|
`termux-open` under plain Termux), `open` on macOS, `start` on Windows, and
|
|
179
214
|
the real system `xdg-open` on Linux.
|
|
180
215
|
|
|
216
|
+
Inside minapk's APK, `MINAPK_WEBVIEW` redirects that: set it to a WebView id
|
|
217
|
+
and a URL is loaded into that WebView and brought to the front (`openWebView`
|
|
218
|
+
then `showWebView`) instead of being handed to the system's default handler.
|
|
219
|
+
|
|
220
|
+
```sh
|
|
221
|
+
MINAPK_WEBVIEW=1 xdg-open https://example.com # in the app WebView, on screen
|
|
222
|
+
export MINAPK_WEBVIEW=1 # ...or for the whole session
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
`0` is the console, so it navigates the terminal page away -- the back key
|
|
226
|
+
returns to it and jsgotty reconnects, but it is not usually what you want.
|
|
227
|
+
`-1` is whichever WebView is in front. Only URLs are redirected: a file path
|
|
228
|
+
always goes to the host's own handler, since WebView cannot read a `file://`
|
|
229
|
+
URL under Buninu's home (`setAllowFileAccess` is false from API 30 on) while
|
|
230
|
+
the host serves that same file through its content:// provider. A value that
|
|
231
|
+
is not a plain integer is reported on stderr and ignored rather than guessed
|
|
232
|
+
at, an unset or empty value keeps the default behavior, and a WebView the host
|
|
233
|
+
does not have falls back to the default handler after saying so.
|
|
234
|
+
|
|
181
235
|
## Export
|
|
182
236
|
|
|
183
237
|
Export the current Buninu installation as a gzip-compressed tar archive:
|
|
@@ -349,6 +403,29 @@ from the directory containing `package.json`.
|
|
|
349
403
|
|
|
350
404
|
Use `--shell <path-or-name>` for a one-time override.
|
|
351
405
|
|
|
406
|
+
## Back key (Android, optional)
|
|
407
|
+
|
|
408
|
+
Set `buninu.backToConsole` in `package.json` to decide what the Android host
|
|
409
|
+
app's back key does while the app WebView (id `1`, see `native-bridge` above)
|
|
410
|
+
is in front and has no page of its own left to go back to. It defaults to
|
|
411
|
+
`true`: back switches to the console WebView, leaving the app WebView loaded
|
|
412
|
+
and running behind it. Set it to `false` and back leaves the app instead.
|
|
413
|
+
|
|
414
|
+
```json
|
|
415
|
+
{
|
|
416
|
+
"buninu": {
|
|
417
|
+
"backToConsole": false
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
This one is read by the host app, not by Buninu itself, so it does nothing
|
|
423
|
+
outside an APK built with
|
|
424
|
+
[minapk](https://www.npmjs.com/package/@drxiaozhi/minapk) -- where it can also
|
|
425
|
+
be set for a single build with `--no-back-to-console`. The host treats a
|
|
426
|
+
missing, unreadable, or non-boolean value as `true`, so nothing here can fail
|
|
427
|
+
in a way that leaves the back key broken.
|
|
428
|
+
|
|
352
429
|
## Process-list helpers
|
|
353
430
|
|
|
354
431
|
The bundled `.bashrc` provides `pspa` (`ps -eo pid,args`) and `pspac`, which
|
|
@@ -101,6 +101,51 @@ export const tts = ttsStatus
|
|
|
101
101
|
// the receiving app on modern Android.
|
|
102
102
|
export const xdgOpen = (target) => call("xdgOpen", [target])
|
|
103
103
|
|
|
104
|
+
// The host has exactly two WebViews, both alive from startup and neither ever
|
|
105
|
+
// created nor closed at runtime: WEBVIEW_CONSOLE (0) is the jsgotty terminal
|
|
106
|
+
// this shell is being rendered in, WEBVIEW_APP (1) is the app WebView, which
|
|
107
|
+
// simply starts out blank and behind. WEBVIEW_CURRENT (-1) means whichever
|
|
108
|
+
// one is in front right now, and is the default wherever an id is optional.
|
|
109
|
+
// All four functions also go by openwv/evalwv/showwv/currwv, both here and
|
|
110
|
+
// over the wire (the Java dispatcher answers to either spelling and lists
|
|
111
|
+
// both in _discover), on the same grounds as getcb/setcb above.
|
|
112
|
+
export const WEBVIEW_CURRENT = -1
|
|
113
|
+
export const WEBVIEW_CONSOLE = 0
|
|
114
|
+
export const WEBVIEW_APP = 1
|
|
115
|
+
|
|
116
|
+
// Loads url into a WebView *without* bringing it to the front, so
|
|
117
|
+
// openWebView(WEBVIEW_APP, url) while the user is looking at the terminal is
|
|
118
|
+
// a plain background load -- showWebView is the only thing that changes what
|
|
119
|
+
// is on screen. Omitting url loads nothing and just reports the id back,
|
|
120
|
+
// which is how -1 gets resolved to a real id. Returns the id loaded into.
|
|
121
|
+
export const openWebView = (id = WEBVIEW_CURRENT, url = "") =>
|
|
122
|
+
call("openWebView", [id, url])
|
|
123
|
+
export const openwv = openWebView
|
|
124
|
+
|
|
125
|
+
// Resolves to the value the expression produced -- a real number/string/
|
|
126
|
+
// object, not a string containing one -- because the host sends
|
|
127
|
+
// evaluateJavascript's own JSON text straight back as the response body.
|
|
128
|
+
// undefined, a function, and a thrown exception all arrive as null: WebView
|
|
129
|
+
// itself does not distinguish them, so return something JSON can carry when
|
|
130
|
+
// you need to tell them apart. A page that never answers is given up on by
|
|
131
|
+
// the host (inside this module's own call timeout) and comes back as an
|
|
132
|
+
// "error: ..." string rather than hanging.
|
|
133
|
+
export const evalWebView = (id, js) => call("evalWebView", [id, js])
|
|
134
|
+
export const evalwv = evalWebView
|
|
135
|
+
|
|
136
|
+
// Brings a WebView to the front. The bottom key bar (Ctrl/Alt/Shift and
|
|
137
|
+
// friends), the volume-key menu and the back key all act on whatever is in
|
|
138
|
+
// front, so this is also what re-points them. An id of -1 -- the default --
|
|
139
|
+
// switches to the next WebView rather than being a no-op, which with two of
|
|
140
|
+
// them is simply a toggle. Returns the id now in front.
|
|
141
|
+
export const showWebView = (id = WEBVIEW_CURRENT) => call("showWebView", [id])
|
|
142
|
+
export const showwv = showWebView
|
|
143
|
+
|
|
144
|
+
// The id of the WebView in front, for code that wants to put something back
|
|
145
|
+
// the way it found it.
|
|
146
|
+
export const currWebView = () => call("currWebView")
|
|
147
|
+
export const currwv = currWebView
|
|
148
|
+
|
|
104
149
|
// `bun native-bridge.js <func> [args...]`, dispatched straight to call(func,
|
|
105
150
|
// args) -- not restricted to toast/clipboardRead/clipboardWrite, exactly like
|
|
106
151
|
// calling rpcraw(func, args) would be. This is a raw RPC, equivalent to a
|
|
@@ -7,9 +7,74 @@
|
|
|
7
7
|
// the default handler" mechanism -- macOS/Windows are real OS
|
|
8
8
|
// commands/APIs, but Linux's is conventionally *also* a command named
|
|
9
9
|
// xdg-open, so this script has to be careful not to find itself on PATH.
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
xdgOpen as bridgeOpen, available, openWebView, showWebView,
|
|
12
|
+
} from "../native-bridge/native-bridge.js"
|
|
11
13
|
const fs = require("fs")
|
|
12
14
|
|
|
15
|
+
// MINAPK_WEBVIEW names *which* of the host app's WebViews to open into (0 is
|
|
16
|
+
// the console, 1 the app WebView, -1 whichever is in front); unset means the
|
|
17
|
+
// unchanged behavior of handing the target to the system's default handler.
|
|
18
|
+
// It is read here rather than being detected by the host, because it is an
|
|
19
|
+
// environment variable of this Bun process tree -- the host app's own process
|
|
20
|
+
// cannot see a value exported in this shell a moment ago, and the whole point
|
|
21
|
+
// of the variable is that it can be.
|
|
22
|
+
//
|
|
23
|
+
// Deliberately never Number()/parseInt(): Number("") is 0, and 0 is a
|
|
24
|
+
// perfectly valid WebView id, so an exported-but-empty MINAPK_WEBVIEW= (the
|
|
25
|
+
// usual way to switch something off) would come out meaning "open in the
|
|
26
|
+
// console" -- i.e. navigate the terminal away. parseInt would likewise turn
|
|
27
|
+
// "1x" into 1. Only a bare optionally-signed integer counts; anything else is
|
|
28
|
+
// reported and treated as unset.
|
|
29
|
+
function webViewIdFromEnv() {
|
|
30
|
+
const raw = (process.env.MINAPK_WEBVIEW ?? "").trim()
|
|
31
|
+
if (raw === "") return null
|
|
32
|
+
if (!/^-?\d+$/.test(raw)) {
|
|
33
|
+
process.stderr.write(
|
|
34
|
+
`xdg-open: ignoring MINAPK_WEBVIEW=${raw} (not an integer WebView id)\n`)
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
return Number(raw)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Only URLs. A filesystem path stays on the bridgeOpen path even when
|
|
41
|
+
// MINAPK_WEBVIEW is set: WebView's setAllowFileAccess defaults to false from
|
|
42
|
+
// API 30 on, so a file:// URL under Buninu's home would load as a blank page,
|
|
43
|
+
// whereas the host's xdgOpen already serves that file properly through its
|
|
44
|
+
// read-only content:// provider.
|
|
45
|
+
const isUrl = (target) => /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(target)
|
|
46
|
+
|
|
47
|
+
// The host answers with the id it actually used -- a JSON number -- and with
|
|
48
|
+
// a string when something went wrong: "error: no such WebView: 9" from the
|
|
49
|
+
// dispatcher, or rpc.mjs's own "Unknown func ..." when the APK is older than
|
|
50
|
+
// these functions and does not list them in _discover. rpc.mjs also *resolves*
|
|
51
|
+
// to an Error object instead of throwing when the fetch itself fails. So
|
|
52
|
+
// success is checked as "is genuinely a number", never by parsing the result:
|
|
53
|
+
// see webViewIdFromEnv above for why a coercing parse is dangerous here.
|
|
54
|
+
//
|
|
55
|
+
// showWebView(-1) means "switch to the next one", not "the one just loaded",
|
|
56
|
+
// so the switch uses the id openWebView resolved and handed back, not the id
|
|
57
|
+
// that was asked for.
|
|
58
|
+
async function openInWebView(id, url) {
|
|
59
|
+
const opened = await openWebView(id, url)
|
|
60
|
+
if (typeof opened !== "number" || !Number.isInteger(opened)) {
|
|
61
|
+
process.stderr.write(
|
|
62
|
+
`xdg-open: MINAPK_WEBVIEW=${id}: ${opened?.message ?? opened}\n`)
|
|
63
|
+
return false
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const shown = await showWebView(opened)
|
|
67
|
+
if (typeof shown !== "number" || !Number.isInteger(shown)) {
|
|
68
|
+
// Loaded but not brought to the front: say so rather than reporting
|
|
69
|
+
// success for something the user cannot see, but do not fall back to the
|
|
70
|
+
// system handler either -- the page really is loaded in that WebView.
|
|
71
|
+
process.stderr.write(
|
|
72
|
+
`xdg-open: loaded in WebView ${opened} but could not switch to it: ` +
|
|
73
|
+
`${shown?.message ?? shown}\n`)
|
|
74
|
+
}
|
|
75
|
+
return true
|
|
76
|
+
}
|
|
77
|
+
|
|
13
78
|
// Buninu's own bin/ is prepended to PATH ahead of any real system binary of
|
|
14
79
|
// the same name (same concern as xclip.js/tts.js), and this script is
|
|
15
80
|
// itself registered as Buninu's "xdg-open" command -- a naive PATH search
|
|
@@ -46,7 +111,14 @@ let ok = false
|
|
|
46
111
|
try {
|
|
47
112
|
if (process.platform === "android") {
|
|
48
113
|
if (available()) {
|
|
49
|
-
|
|
114
|
+
const webViewId = webViewIdFromEnv()
|
|
115
|
+
// A failed WebView open falls back to the system handler rather than
|
|
116
|
+
// failing outright: the request was "open this", and MINAPK_WEBVIEW
|
|
117
|
+
// only says where it would be preferred. openInWebView has already
|
|
118
|
+
// explained on stderr why it could not.
|
|
119
|
+
ok = webViewId !== null && isUrl(target)
|
|
120
|
+
? (await openInWebView(webViewId, target)) || (await bridgeOpen(target))
|
|
121
|
+
: await bridgeOpen(target)
|
|
50
122
|
} else if (Bun.which("termux-open")) {
|
|
51
123
|
// Plain Termux (no minapk) still reports platform "android" but has
|
|
52
124
|
// no PKG_BRIDGE_SOCK -- termux-open (Termux:API) does the same
|
package/bin/init.js
CHANGED
|
@@ -258,7 +258,7 @@ async function detectEnvironment() {
|
|
|
258
258
|
return {
|
|
259
259
|
name: "linux",
|
|
260
260
|
shell:
|
|
261
|
-
await findCommand(process.env.SHELL) ||
|
|
261
|
+
// await findCommand(process.env.SHELL) ||
|
|
262
262
|
Bun.which("bash") ||
|
|
263
263
|
Bun.which("sh") ||
|
|
264
264
|
"/bin/sh",
|
|
@@ -269,10 +269,11 @@ async function detectEnvironment() {
|
|
|
269
269
|
return {
|
|
270
270
|
name: "android",
|
|
271
271
|
shell:
|
|
272
|
-
await findCommand(process.env.SHELL) ||
|
|
272
|
+
// await findCommand(process.env.SHELL) ||
|
|
273
273
|
(await pathExists("/system/bin/sh") ? "/system/bin/sh" : null) ||
|
|
274
|
+
Bun.which("bash") ||
|
|
274
275
|
Bun.which("sh") ||
|
|
275
|
-
"sh",
|
|
276
|
+
"/bin/sh",
|
|
276
277
|
};
|
|
277
278
|
}
|
|
278
279
|
|
|
@@ -280,7 +281,7 @@ async function detectEnvironment() {
|
|
|
280
281
|
return {
|
|
281
282
|
name: "macos",
|
|
282
283
|
shell:
|
|
283
|
-
await findCommand(process.env.SHELL) ||
|
|
284
|
+
// await findCommand(process.env.SHELL) ||
|
|
284
285
|
Bun.which("zsh") ||
|
|
285
286
|
Bun.which("bash") ||
|
|
286
287
|
Bun.which("sh") ||
|
|
@@ -292,7 +293,7 @@ async function detectEnvironment() {
|
|
|
292
293
|
return {
|
|
293
294
|
name: "linux",
|
|
294
295
|
shell:
|
|
295
|
-
await findCommand(process.env.SHELL) ||
|
|
296
|
+
// await findCommand(process.env.SHELL) ||
|
|
296
297
|
Bun.which("bash") ||
|
|
297
298
|
Bun.which("sh") ||
|
|
298
299
|
"/bin/sh",
|
|
@@ -302,7 +303,7 @@ async function detectEnvironment() {
|
|
|
302
303
|
return {
|
|
303
304
|
name: `unix:${process.platform}`,
|
|
304
305
|
shell:
|
|
305
|
-
await findCommand(process.env.SHELL) ||
|
|
306
|
+
// await findCommand(process.env.SHELL) ||
|
|
306
307
|
Bun.which("bash") ||
|
|
307
308
|
Bun.which("sh") ||
|
|
308
309
|
"/bin/sh",
|
package/demo-server.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
// Smallest end-to-end demo of the WebView bridge: serve one fixed page on a
|
|
4
|
+
// port nobody picked in advance, then have xdg-open put it in the host app's
|
|
5
|
+
// app WebView and switch to it. Run it from a Buninu shell inside minapk's
|
|
6
|
+
// APK:
|
|
7
|
+
//
|
|
8
|
+
// bun demo-server.js
|
|
9
|
+
//
|
|
10
|
+
// Outside such an APK it still runs and still serves; xdg-open just falls
|
|
11
|
+
// back to whatever that platform's default handler is (see apps/xdg-open).
|
|
12
|
+
|
|
13
|
+
const BODY = "Hello 你好 こんにちは"
|
|
14
|
+
|
|
15
|
+
// port: 0 asks the OS for a free port instead of guessing one and racing
|
|
16
|
+
// whatever already has it -- Buninu's own launcher picks jsgotty's port the
|
|
17
|
+
// same way. The real number is only knowable after the listener exists, so
|
|
18
|
+
// the URL is built from server.port, never from a constant.
|
|
19
|
+
// hostname stays on loopback: this is a demo page for the WebView on this
|
|
20
|
+
// device, not something other machines on the network should reach.
|
|
21
|
+
const server = Bun.serve({
|
|
22
|
+
hostname: "127.0.0.1",
|
|
23
|
+
port: 0,
|
|
24
|
+
fetch() {
|
|
25
|
+
// charset is not optional here: without it the WebView guesses an
|
|
26
|
+
// encoding for the response, and the CJK half of BODY is what shows up
|
|
27
|
+
// as mojibake when it guesses wrong.
|
|
28
|
+
return new Response(BODY, {
|
|
29
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
|
30
|
+
})
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// 127.0.0.1 rather than localhost: the listener is bound to the v4 loopback
|
|
35
|
+
// address specifically, while "localhost" can resolve to ::1 first and give
|
|
36
|
+
// the WebView a connection refused before it ever tries v4.
|
|
37
|
+
const url = `http://127.0.0.1:${server.port}`
|
|
38
|
+
console.log(`demo-server: serving ${JSON.stringify(BODY)} at ${url}`)
|
|
39
|
+
|
|
40
|
+
// Buninu puts its own bin/ on PATH, so "xdg-open" normally resolves to
|
|
41
|
+
// apps/xdg-open through the shloader. Fall back to running the script
|
|
42
|
+
// directly for the case where this file is run outside a Buninu shell (a bare
|
|
43
|
+
// `bun demo-server.js` in a checkout), where that PATH entry is not there.
|
|
44
|
+
const xdgOpen = Bun.which("xdg-open")
|
|
45
|
+
const command = xdgOpen
|
|
46
|
+
? [xdgOpen, url]
|
|
47
|
+
: [process.execPath, `${import.meta.dir}/apps/xdg-open/xdg-open.js`, url]
|
|
48
|
+
|
|
49
|
+
// MINAPK_WEBVIEW is read by xdg-open itself, not by the host app -- it names
|
|
50
|
+
// which WebView to load into (1 is the app WebView) and brings it to the
|
|
51
|
+
// front. Passed to this one child rather than exported, so it changes nothing
|
|
52
|
+
// for the rest of the session.
|
|
53
|
+
const child = Bun.spawn(command, {
|
|
54
|
+
env: { ...process.env, MINAPK_WEBVIEW: "1" },
|
|
55
|
+
stdout: "inherit",
|
|
56
|
+
stderr: "inherit",
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
if (await child.exited !== 0)
|
|
60
|
+
console.error(`demo-server: ${command[0]} exited with ${child.exitCode}; page is still served at ${url}`)
|
|
61
|
+
|
|
62
|
+
// The server has to outlive the open: the WebView fetches the page after
|
|
63
|
+
// xdg-open has already returned. Ctrl-C to stop.
|
|
64
|
+
console.log("demo-server: Ctrl-C to stop")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "buninu",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Buninu is a portable, self-bootstrapping Unix-like userspace built on Bun that runs across operating systems",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./bin/init.js",
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"macos": null,
|
|
24
24
|
"windows": null
|
|
25
25
|
},
|
|
26
|
-
"exitAfterCmd": false
|
|
26
|
+
"exitAfterCmd": false,
|
|
27
|
+
"backToConsole": true
|
|
27
28
|
},
|
|
28
29
|
"scripts": {
|
|
29
30
|
"start": "bun ./apps/jsgotty/gotty.js --reconnect -r -w --webgl -a 127.0.0.1",
|