Add edit program

This commit is contained in:
Raven Scott
2026-04-03 21:59:05 -04:00
parent 57b1296981
commit 32d3eaa833
40 changed files with 7211 additions and 412 deletions
+4 -4
View File
@@ -180,7 +180,7 @@ These files are **read from disk by the seeder** and written into the system dri
- **Staged as**: `/bin/help` - **Staged as**: `/bin/help`
- **Contract**: `async function run(ctx, _argv)`. - **Contract**: `async function run(ctx, _argv)`.
- **Behavior**: Logs `Commands: help, echo <text>, exit`. - **Behavior**: Logs a compact list of **`/bin`** names, builtins, and pointers to **`man`** / identity commands (not an exhaustive tutorial; see **`man edit`** / **`man nano`** for the TTY editor).
### 9.4 [kernel/etc/os-release](kernel/etc/os-release) ### 9.4 [kernel/etc/os-release](kernel/etc/os-release)
@@ -430,7 +430,7 @@ Pear-safe path resolution (same idea as Holepunch [pear-rti](https://github.com/
- If **`argv[0]`** is **`wget`** under the same basename rules, **delegates** to **`runWgetCli`** in [`wget-cli.js`](packages/bare-os-booter/lib/wget-cli.js) (HTTP download via Fetch / **bare-fetch**; manual **`wget(1)`** in the merged JSON DB). - If **`argv[0]`** is **`wget`** under the same basename rules, **delegates** to **`runWgetCli`** in [`wget-cli.js`](packages/bare-os-booter/lib/wget-cli.js) (HTTP download via Fetch / **bare-fetch**; manual **`wget(1)`** in the merged JSON DB).
- If `argv[0]` contains `/`, resolves with `ctx.vfs.resolveLogical`, `route`, loads script bytes from the routed Hyperdrive (`get` with `follow`). - If `argv[0]` contains `/`, resolves with `ctx.vfs.resolveLogical`, `route`, loads script bytes from the routed Hyperdrive (`get` with `follow`).
- Else walks `$PATH` (`ctx.vfs.env.PATH`, default `/bin`), joining each directory with `unix-path-resolve(dir, cmd)` (not three-argument resolve), loads from **system** `ctx.drive` only. - Else walks `$PATH` (`ctx.vfs.env.PATH`, default `/bin`), joining each directory with `unix-path-resolve(dir, cmd)` (not three-argument resolve), loads from **system** `ctx.drive` only.
- Builds `AsyncFunction('ctx','argv', ...)` requiring `run`, invokes `run(ctx, argv)`. - Builds `AsyncFunction('ctx','argv', ...)` with the script source plus `if (typeof run === 'function') await run(ctx, argv)` (top-level statements run first; optional `run` matches `/bin` utilities).
- Unknown command: `ctx.console.log('unknown command: ...')`. - Unknown command: `ctx.console.log('unknown command: ...')`.
**`resolveBinInPath(ctx, name)`** — returns the first **`PATH`** hit on the system drive (absolute `/bin/...` path string) or **`null`**; used by shell **`command -v`** / **`type`**. **`resolveBinInPath(ctx, name)`** — returns the first **`PATH`** hit on the system drive (absolute `/bin/...` path string) or **`null`**; used by shell **`command -v`** / **`type`**.
@@ -464,9 +464,9 @@ Pear-safe path resolution (same idea as Holepunch [pear-rti](https://github.com/
### 12.10 Package `bare-os-coreutils` ### 12.10 Package `bare-os-coreutils`
- **[packages/bare-os-coreutils/lib/commands.mjs](packages/bare-os-coreutils/lib/commands.mjs)** — **`COREUTILS_COMMANDS`**: authoritative sorted `/bin` names for **`build.mjs`** and the manual database builder (keeps the image and **`man`** coverage in sync). - **[packages/bare-os-coreutils/lib/commands.mjs](packages/bare-os-coreutils/lib/commands.mjs)** — **`COREUTILS_COMMANDS`**: authoritative sorted `/bin` names for **`build.mjs`** and the manual database builder (keeps the image and **`man`** coverage in sync).
- **[packages/bare-os-coreutils/build.mjs](packages/bare-os-coreutils/build.mjs)** — `export async function build()`: runs **`scripts/build-man-db.mjs`** (validates **`man/pages/*.json`**, writes **`kernel/share/man/man.json`** and the same path under **`packages/bare-os-seeder/kernel/share/man/`**); then for each command concatenates `lib/runtime.js`, optional **`preamble`** libs (`sed``lib/sed-engine.js`, `awk``lib/awk-engine.js`, **`man`** → `lib/man-render.js`), then `src/<name>.js`; writes to `kernel/bin/<name>` and `packages/bare-os-seeder/kernel/bin/<name>`. CLI: `node build.mjs` when executed as main. - **[packages/bare-os-coreutils/build.mjs](packages/bare-os-coreutils/build.mjs)** — `export async function build()`: runs **`scripts/build-man-db.mjs`** (validates **`man/pages/*.json`**, writes **`kernel/share/man/man.json`** and the same path under **`packages/bare-os-seeder/kernel/share/man/`**); then for each command concatenates `lib/runtime.js`, optional **`preamble`** libs (`sed``lib/sed-engine.js`, `awk``lib/awk-engine.js`, **`man`** → `lib/man-render.js`, **`ls`** / **`dircolors`** → lscolors helpers, **`edit`** / **`nano`** → `lib/edit-*.js` + shared TUI), then `src/<name>.js` (**`nano`** reuses **`src/edit.js`**); writes to `kernel/bin/<name>` and `packages/bare-os-seeder/kernel/bin/<name>`. CLI: `node build.mjs` when executed as main.
- **Manual pages** — Authoring: **`packages/bare-os-coreutils/man/pages/<name>.json`**; schema: **`man/schema.json`**. Optional **`examples`** (cheat.sh-style) and **`descriptionMode`**: **`preserve`** for preformatted text. **`scripts/ingest-handbook-for-man.mjs`** merges every **`handbook/*.md`** as **`man(7)`** at build time (**`man handbook`**, **`man handbook-01-introduction`**, …). Regenerate JSON stubs with **`node packages/bare-os-coreutils/scripts/seed-man-pages.mjs`**. Runtime: **`/bin/man`** reads **`/share/man/man.json`**. **Handbook:** [handbook/10-manpages-and-online-help.md](handbook/10-manpages-and-online-help.md). - **Manual pages** — Authoring: **`packages/bare-os-coreutils/man/pages/<name>.json`**; schema: **`man/schema.json`**. Optional **`examples`** (cheat.sh-style) and **`descriptionMode`**: **`preserve`** for preformatted text. **`scripts/ingest-handbook-for-man.mjs`** merges every **`handbook/*.md`** as **`man(7)`** at build time (**`man handbook`**, **`man handbook-01-introduction`**, …). Regenerate JSON stubs with **`node packages/bare-os-coreutils/scripts/seed-man-pages.mjs`**. Runtime: **`/bin/man`** reads **`/share/man/man.json`**. **Handbook:** [handbook/10-manpages-and-online-help.md](handbook/10-manpages-and-online-help.md).
- **Commands** (sources under `src/`, same order as **`COREUTILS_COMMANDS`** in [`commands.mjs`](packages/bare-os-coreutils/lib/commands.mjs)): `awk`, `basename`, `cat`, `chgrp`, `chmod`, `chown`, `cksum`, `clear`, `cp`, `crontab`, `cut`, `date`, `dirname`, `dircolors`, `du`, `echo`, `env`, `exit`, `false`, `find`, `getconf`, `git-pear`, `grep`, `head`, `hdms`, `help`, `hostname`, `id`, `jq`, `ln`, `login`, `logout`, `logname`, `ls`, `man`, `mkdir`, `mkfifo`, `mktemp`, `mv`, `nl`, `od`, `pathchk`, `printenv`, `printf`, `pwd`, `readlink`, `rm`, `rmdir`, `savevault`, `sed`, `seq`, `sleep`, `sort`, `stat`, `tail`, `tee`, `test`, `theme`, `time`, `touch`, `tr`, `true`, `tty`, `uname`, `wc`, `which`, `whoami`, `xargs`. Each built script begins with **`BARE_OS_BIN_API`** in the concatenated prelude; root **`pretest`** runs **[`scripts/verify-kernel-seeder-parity.mjs`](scripts/verify-kernel-seeder-parity.mjs)** to keep **`kernel/bin/*`** and **`packages/bare-os-seeder/kernel/bin/*`** in sync and to require that pragma on every staged binary. Scripts are plain `async function run(ctx, argv)` using `ctx.vfs`, `ctx.drive`, `ctx.b4a`, `ctx.console`, optional `bareStdin(ctx)`, optional `ctx.runBinCommand` — no ESM `import` in `src/` (Bare-safe `AsyncFunction` load). **Booter-delegated** (stubs under `kernel/bin/`, logic in `packages/bare-os-booter/lib/`): `systemctl`, `journalctl` (bare-initd control; `bare-initctl` alias; see [handbook/04-the-booter-runtime.md](handbook/04-the-booter-runtime.md)). **Narrative reference:** [handbook/09-posix-utilities-shell-and-vfs.md](handbook/09-posix-utilities-shell-and-vfs.md). - **Commands** (sources under `src/`, same order as **`COREUTILS_COMMANDS`** in [`commands.mjs`](packages/bare-os-coreutils/lib/commands.mjs)): `awk`, `basename`, `cat`, `chgrp`, `chmod`, `chown`, `cksum`, `clear`, `cp`, `crontab`, `cut`, `date`, `dirname`, `dircolors`, `du`, `edit`, `echo`, `env`, `exit`, `false`, `find`, `getconf`, `git-pear`, `grep`, `head`, `hdms`, `help`, `hostname`, `id`, `jq`, `ln`, `login`, `logout`, `logname`, `ls`, `man`, `mkdir`, `mkfifo`, `mktemp`, `mv`, `nano`, `nl`, `od`, `pathchk`, `printenv`, `printf`, `pwd`, `readlink`, `rm`, `rmdir`, `savevault`, `sed`, `seq`, `sleep`, `sort`, `stat`, `tail`, `tee`, `test`, `theme`, `time`, `touch`, `tr`, `true`, `tty`, `uname`, `wc`, `which`, `whoami`, `xargs`. The interactive TTY editor is **`edit`**; **`nano`** is the same built script under **`/bin/nano`**, and the default shell maps **`nano``edit`** (see **`defaultShellAliases`** in [`shell.js`](packages/bare-os-booter/lib/shell.js)). Each built script begins with **`BARE_OS_BIN_API`** in the concatenated prelude; root **`pretest`** runs **[`scripts/verify-kernel-seeder-parity.mjs`](scripts/verify-kernel-seeder-parity.mjs)** to keep **`kernel/bin/*`** and **`packages/bare-os-seeder/kernel/bin/*`** in sync and to require that pragma on every staged binary. Scripts are plain `async function run(ctx, argv)` using `ctx.vfs`, `ctx.drive`, `ctx.b4a`, `ctx.console`, optional `bareStdin(ctx)`, optional `ctx.runBinCommand` — no ESM `import` in `src/` (Bare-safe `AsyncFunction` load). **Booter-delegated** (stubs under `kernel/bin/`, logic in `packages/bare-os-booter/lib/`): `systemctl`, `journalctl` (bare-initd control; `bare-initctl` alias; see [handbook/04-the-booter-runtime.md](handbook/04-the-booter-runtime.md)). **Narrative reference:** [handbook/09-posix-utilities-shell-and-vfs.md](handbook/09-posix-utilities-shell-and-vfs.md).
### 12.11 [packages/bare-os-booter/test.js](packages/bare-os-booter/test.js) and [test.identity.js](packages/bare-os-booter/test.identity.js) ### 12.11 [packages/bare-os-booter/test.js](packages/bare-os-booter/test.js) and [test.identity.js](packages/bare-os-booter/test.identity.js)
@@ -52,9 +52,9 @@ Two entry shapes matter:
| Entry | Where it lives | Parameters | How it is invoked | | Entry | Where it lives | Parameters | How it is invoked |
| ----------------------------------- | -------------------------------------------------- | ---------------------------- | ----------------------------------------------------------------- | | ----------------------------------- | -------------------------------------------------- | ---------------------------- | ----------------------------------------------------------------- |
| **`async function start(ctx)`** | `/boot/init.js` on the **system** drive | `ctx` | `runKernelFromSource` wraps the source and calls `start(ctx)` | | **`async function start(ctx)`** | `/boot/init.js` on the **system** drive | `ctx` | `runKernelFromSource` wraps the source and calls `start(ctx)` |
| **`async function run(ctx, argv)`** | `/bin/*`, or a `*.js` file resolved from the shell | `ctx`, `argv` (string array) | `runScriptFromSource` wraps the source and calls `run(ctx, argv)` | | **`async function run(ctx, argv)`** (optional for user scripts) | `/bin/*` always; or a `*.js` file resolved from the shell | `ctx`, `argv` (string array) | `runScriptFromSource` runs the file body, then **awaits** `run(ctx, argv)` if defined |
The booter **injects** `ctx` and `argv`; your source file must **define** the named function. Top-level `import` is invalid in that evaluated string because the engine is not loading an ES module—it is compiling a function body. The booter **injects** `ctx` and `argv`. Kernel **`start`** is required; for shell scripts, top-level statements may stand alone, or you may **define** **`run`** like **`/bin`** utilities. Top-level `import` is invalid in that evaluated string because the engine is not loading an ES module—it is compiling a function body.
Shebang lines (`#!/usr/bin/env bare`) are stripped before compile ([`stripShebang`](../packages/bare-os-booter/lib/kernel-runner.js)) so the first token the parser sees is valid JavaScript. Shebang lines (`#!/usr/bin/env bare`) are stripped before compile ([`stripShebang`](../packages/bare-os-booter/lib/kernel-runner.js)) so the first token the parser sees is valid JavaScript.
@@ -88,7 +88,7 @@ A script you place in `~/exploit.js` is **your** code; the booter will still `As
| Goal | Use | | Goal | Use |
| ------------------------------------------------------ | ---------------------------------------------------------------- | | ------------------------------------------------------ | ---------------------------------------------------------------- |
| Add a new **`/bin`** command shipped with the OS image | **In-image** pattern: coreutils `src/*.js` + build (Chapter 6) | | Add a new **`/bin`** command shipped with the OS image | **In-image** pattern: coreutils `src/*.js` + build (Chapter 6) |
| One-off automation in your home directory | **In-image** `run(ctx, argv)` script; no `import` (Chapters 45) | | One-off automation in your home directory | **In-image** script (top-level and/or optional `run`); no `import` (Chapters 45) |
| Change how networking, HDMS, or the REPL works | **Host** booter package (ESM) | | Change how networking, HDMS, or the REPL works | **Host** booter package (ESM) |
| Publish or replicate the system image | **Host** seeder package | | Publish or replicate the system image | **Host** seeder package |
+3 -3
View File
@@ -6,7 +6,7 @@ This chapter is the practical “how do I run my own `.js` file?” guide. Resol
## The entrypoint contract (again) ## The entrypoint contract (again)
Your script file must define: The booter evaluates your file as the body of an async function with parameters **`ctx`** and **`argv`**. Top-level statements run first (like a small **Node** script). Optionally, define a top-level **`run`** for the same contract as **`/bin`** utilities:
```js ```js
async function run(ctx, argv) { async function run(ctx, argv) {
@@ -20,11 +20,11 @@ The booter wraps the file body in:
new AsyncFunction( new AsyncFunction(
'ctx', 'ctx',
'argv', 'argv',
source + '\nif (typeof run !== "function") throw ...\nreturn run(ctx, argv)\n' source + '\nif (typeof run === "function") await run(ctx, argv)\n'
) )
``` ```
So **`run`** must exist at the top level of the evaluated string—same as `/bin` utilities. If **`run`** exists, it is **awaited** after the rest of the file. **`/bin`** commands always define **`run`**; home-directory scripts may use top-level code only (e.g. **`console.log(...)`**).
--- ---
@@ -21,7 +21,7 @@ Shared helpers live in **`packages/bare-os-coreutils/lib/`** and are **prepended
2. **Register** the name in **`packages/bare-os-coreutils/lib/commands.mjs`** (`COREUTILS_COMMANDS`—keep sorted). 2. **Register** the name in **`packages/bare-os-coreutils/lib/commands.mjs`** (`COREUTILS_COMMANDS`—keep sorted).
3. **Add a man page** `packages/bare-os-coreutils/man/pages/foo.json` (build fails if missing). 3. **Add a man page** `packages/bare-os-coreutils/man/pages/foo.json` (build fails if missing).
4. **Optional:** seed examples via **`scripts/seed-man-pages.mjs`** or edit JSON directly. 4. **Optional:** seed examples via **`scripts/seed-man-pages.mjs`** or edit JSON directly.
5. **Optional preamble:** if `foo` needs a large engine file, add to **`preamble`** in [`build.mjs`](../packages/bare-os-coreutils/build.mjs): 5. **Optional preamble:** if `foo` needs a large engine file (or several helpers, like **`edit`** / **`nano`** with **`lib/edit-*.js`**), add to **`preamble`** in [`build.mjs`](../packages/bare-os-coreutils/build.mjs):
```js ```js
const preamble = { const preamble = {
@@ -58,7 +58,6 @@ Pear uses bundled **`node_modules`**; if resolution fails, run **`node scripts/e
| Symptom | Likely cause | | Symptom | Likely cause |
| ------------------------------------------- | --------------------------------------------------------------------------- | | ------------------------------------------- | --------------------------------------------------------------------------- |
| `missing run()` | Script lacks `async function run(ctx, argv)` top-level |
| `unknown command` | Name not on system PATH and not a resolvable `*.js` | | `unknown command` | Name not on system PATH and not a resolvable `*.js` |
| `invalid manual database` | Forgot to build coreutils after changing commands list | | `invalid manual database` | Forgot to build coreutils after changing commands list |
| `kernel/bin missing BARE_OS_BIN_API pragma` | Rebuild coreutils or add pragma to hand-maintained **`kernel/bin/*`** stubs | | `kernel/bin missing BARE_OS_BIN_API pragma` | Rebuild coreutils or add pragma to hand-maintained **`kernel/bin/*`** stubs |
+1 -1
View File
@@ -28,7 +28,7 @@ Bare OS picks: **one swarm topic** for the project, **Protomux** channels for co
| **Personal drive** | Separate Hyperdrive (Corestore namespace) for `$HOME`, `/.bare`, cron, logs | | **Personal drive** | Separate Hyperdrive (Corestore namespace) for `$HOME`, `/.bare`, cron, logs |
| **MBR** | 512 bytes: magic `BIOS` + embedded Hyperdrive **public keys** (primary + optional failover) | | **MBR** | 512 bytes: magic `BIOS` + embedded Hyperdrive **public keys** (primary + optional failover) |
| **Kernel** | `/boot/init.js``async function start(ctx)`; not a microkernel, a **session loop** | | **Kernel** | `/boot/init.js``async function start(ctx)`; not a microkernel, a **session loop** |
| **/bin** | Small JS programs (`async function run(ctx, argv)`) built from **bare-os-coreutils** | | **/bin** | Small JS programs (`async function run(ctx, argv)`) built from **bare-os-coreutils** — includes a TTY editor (**`edit`**, **`nano`**) and usual POSIX-style tools |
| **VFS** | Booter-provided path layer: routes paths under `$HOME` to the **personal** drive, else **system** | | **VFS** | Booter-provided path layer: routes paths under `$HOME` to the **personal** drive, else **system** |
| **ctx** | Context object passed to kernel and commands: `vfs`, `console`, `execLine`, identity hooks, etc. | | **ctx** | Context object passed to kernel and commands: `vfs`, `console`, `execLine`, identity hooks, etc. |
| **Guest** | Default session before `login` — predictable `HOME=/home/guest`, no Ed25519 identity | | **Guest** | Default session before `login` — predictable `HOME=/home/guest`, no Ed25519 identity |
+2 -2
View File
@@ -89,7 +89,7 @@ Work is sequenced for **POSIX/script ergonomics first**, then networking and lon
- Tokenizes words, quotes, escapes, **`$VAR`**, pipelines **`|`**, redirections **`>` / `>>` / `<`**. - Tokenizes words, quotes, escapes, **`$VAR`**, pipelines **`|`**, redirections **`>` / `>>` / `<`**.
- Builtins: **`alias`**, **`unalias`**, **`cd`**, **`export`**, **`unset`**, **`readonly`**, **`umask`**, **`:`**, **`command`**, **`type`**, **`login`**, **`logout`**, **`exit`** — plus external commands via **`runBinCommand`**. **`readonly`** blocks **`export`** and assignment writes to the same name; **`command -v` / `-V`** and **`type`** use **`resolveBinInPath`** for **`PATH`** lookup. - Builtins: **`alias`**, **`unalias`**, **`cd`**, **`export`**, **`unset`**, **`readonly`**, **`umask`**, **`:`**, **`command`**, **`type`**, **`login`**, **`logout`**, **`exit`** — plus external commands via **`runBinCommand`**. **`readonly`** blocks **`export`** and assignment writes to the same name; **`command -v` / `-V`** and **`type`** use **`resolveBinInPath`** for **`PATH`** lookup.
- First-word **aliases** (defaults like **`ll``ls -la`**) expand after **`$VAR`** substitution; **`alias`** / **`unalias`** match the restricted **`~/.barerc`** syntax (not full POSIX **`sh`**). - First-word **aliases** (defaults like **`ll``ls -la`**, **`nano``edit`**) expand after **`$VAR`** substitution; **`alias`** / **`unalias`** match the restricted **`~/.barerc`** syntax (not full POSIX **`sh`**).
- Pipes capture **`console.log`** into the next stage or a string sink. - Pipes capture **`console.log`** into the next stage or a string sink.
**`runBinCommand`** (`lib/kernel-runner.js`): **`runBinCommand`** (`lib/kernel-runner.js`):
@@ -98,7 +98,7 @@ Work is sequenced for **POSIX/script ergonomics first**, then networking and lon
2. Else if the name **ends with `.js`** — resolve **`$PWD/name.js`** first (same as explicit `./` for many cases). 2. Else if the name **ends with `.js`** — resolve **`$PWD/name.js`** first (same as explicit `./` for many cases).
3. Else walk **`PATH`** on the **system** drive only. 3. Else walk **`PATH`** on the **system** drive only.
**`runScriptFromSource`** strips an optional **`#!`** line, requires **`async function run(ctx, argv)`**, and **catches** errors—logs to **`ctx.console.error`** without unwinding the kernel loop. **`runScriptFromSource`** strips an optional **`#!`** line, runs the script body as top-level code in an async function, then **awaits** a top-level **`run(ctx, argv)`** if one is defined (optional for user scripts; **`/bin`** utilities always define **`run`**). It **catches** errors—logs to **`ctx.console.error`** without unwinding the kernel loop.
**`runKernelFromSource`** requires **`async function start(ctx)`** at the top level of `/boot/init.js`. **`runKernelFromSource`** requires **`async function start(ctx)`** at the top level of `/boot/init.js`.
+3 -2
View File
@@ -51,7 +51,7 @@ Replacing **`/boot/init.js`** on the system image is supported: the booter loads
``` ```
packages/bare-os-coreutils/lib/runtime.js packages/bare-os-coreutils/lib/runtime.js
+ +
(optional) packages/bare-os-coreutils/lib/<engine>.js ← sed-engine, awk-engine (optional) packages/bare-os-coreutils/lib/<engine>.js ← sed-engine, awk-engine, edit-*.js, …
+ +
packages/bare-os-coreutils/src/<cmd>.js packages/bare-os-coreutils/src/<cmd>.js
↓ (build.mjs, see preamble map) ↓ (build.mjs, see preamble map)
@@ -59,7 +59,7 @@ kernel/bin/<cmd>
packages/bare-os-seeder/kernel/bin/<cmd> ← Pear vendored copy packages/bare-os-seeder/kernel/bin/<cmd> ← Pear vendored copy
``` ```
**Rule:** no `import` in `src/*.js` — only `async function run(ctx, argv)` (shared helpers live in **`lib/runtime.js`**). Large **`sed`** and **`awk`** bodies live in **`lib/*-engine.js`** and are prepended at build time (same global scope as **`run`**). **Rule:** no `import` in `src/*.js` — only `async function run(ctx, argv)` (shared helpers live in **`lib/runtime.js`**). Large **`sed`** and **`awk`** bodies live in **`lib/*-engine.js`** and are prepended at build time (same global scope as **`run`**). **`edit`** and **`nano`** share the same **`src/edit.js`** and **`lib/edit-*.js`** preamble; **`/bin/nano`** is a separate staged name for familiarity (default shell alias **`nano``edit`**).
**Full POSIX-style catalog, stubs, and divergence notes:** [Chapter 9 — POSIX utilities, shell, and VFS](09-posix-utilities-shell-and-vfs.md). **Full POSIX-style catalog, stubs, and divergence notes:** [Chapter 9 — POSIX utilities, shell, and VFS](09-posix-utilities-shell-and-vfs.md).
@@ -80,6 +80,7 @@ Hyperdrive entries carry optional **`metadata.bareOs`** (mode, uid, gid, names,
| `crontab` | `-l` list, `-r` remove, `<file>` install (`~/.crontab`; writes need login) | | `crontab` | `-l` list, `-r` remove, `<file>` install (`~/.crontab`; writes need login) |
| `date` | Date/time | | `date` | Date/time |
| `echo`, `printf`-like simplicity | Args to stdout | | `echo`, `printf`-like simplicity | Args to stdout |
| `edit`, `nano` | TTY full-screen buffer editor (same source for both **`/bin`** names; stock alias **`nano``edit`**); requires real TTY — see [ch. 9 §5.2a](09-posix-utilities-shell-and-vfs.md) |
| `env`, `printenv` | Environment | | `env`, `printenv` | Environment |
| `exit` | Sets exit code / session end via booter | | `exit` | Sets exit code / session end via booter |
| `false`, `true` | Status | | `false`, `true` | Status |
+9 -2
View File
@@ -129,6 +129,13 @@ Sources: **`packages/bare-os-coreutils/src/<name>.js`**. **Authoritative list:**
| **`awk`** | Substantial interpreter — see §7. | | **`awk`** | Substantial interpreter — see §7. |
| **`grep`** | **`-F`** fixed strings, **`-i`**, **`-v`**, **`-w`**, **`-x`**, **`-n`**, **`-c`**, **`-l`**, **`-o`**, **`-m`**, **`-e`**, **`-f`**, etc.; JS **`RegExp`** (not PCRE / full GNU). | | **`grep`** | **`-F`** fixed strings, **`-i`**, **`-v`**, **`-w`**, **`-x`**, **`-n`**, **`-c`**, **`-l`**, **`-o`**, **`-m`**, **`-e`**, **`-f`**, etc.; JS **`RegExp`** (not PCRE / full GNU). |
### 5.2a TTY text editor (`edit` / `nano`)
| Command | Notes |
| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`edit`** | Full-screen in-terminal editor over **`ctx.vfs`**: multi-line buffer, search / goto / save-as, optional syntax highlighting. Requires **`stdout.isTTY`**; exits with a clear error if stdin/stdout is not a TTY. Source: **`src/edit.js`** with preamble **`lib/edit-ansi.js`**, **`edit-highlight.js`**, **`edit-buffer.js`**, **`edit-key-parse.js`**, **`edit-tui.js`**. |
| **`nano`** | Same built artifact as **`edit`** (**`build.mjs`** maps **`nano`** → **`src/edit.js`** and the same preamble). **`/bin/nano`** is installed for muscle memory; the default shell alias **`nano``edit`** (see **`defaultShellAliases`** in **`shell.js`**) makes **`nano`** invoke that binary. **`man edit`** and **`man nano`** describe flags and keys. |
### 5.3 Discovery and measurement ### 5.3 Discovery and measurement
| Command | Notes | | Command | Notes |
@@ -182,8 +189,8 @@ All other commands from **`build.mjs`** not listed here follow the summaries in
**[`build.mjs`](../packages/bare-os-coreutils/build.mjs)** concatenates: **[`build.mjs`](../packages/bare-os-coreutils/build.mjs)** concatenates:
1. **`lib/runtime.js`** 1. **`lib/runtime.js`**
2. Optional extra libs from the **`preamble`** map (**`sed``sed-engine.js`**, **`awk``awk-engine.js`**) 2. Optional extra libs from the **`preamble`** map (**`sed``sed-engine.js`**, **`awk``awk-engine.js`**, **`jq``jq-engine.js`**, **`man``man-render.js`**, **`ls` / `dircolors` → lscolors**, **`edit` / `nano``edit-*.js`** TUI stack)
3. **`src/<cmd>.js`** 3. **`src/<cmd>.js`** ( **`nano`** uses **`src/edit.js`** )
There is still **no `import`** in **`src/*.js`** — large utilities are **vendored as plain script** chunks in **`lib/`**. There is still **no `import`** in **`src/*.js`** — large utilities are **vendored as plain script** chunks in **`lib/`**.
+1 -1
View File
@@ -91,7 +91,7 @@ The merged **`man.json`** adds **`schemaVersion`**, **`generatedAt`**, **`pages`
## Relationship to **`help`** ## Relationship to **`help`**
**`/bin/help`** prints a **compact one-screen** list of builtins and **`/bin`** names. **`man`** is the **long-form** reference. Users are encouraged to run **`man handbook`** for the full narrative handbook, **`man bare-os-shell`** for builtins, and **`man <command>`** for **`/bin`** utilities. **`/bin/help`** prints a **compact one-screen** list of builtins and **`/bin`** names (including **`edit`** and **`nano`**). **`man`** is the **long-form** reference — e.g. **`man edit`**, **`man nano`**, **`man bare-os-shell`**. Users are encouraged to run **`man handbook`** for the full narrative handbook and **`man <command>`** for each utility.
--- ---
+1 -1
View File
@@ -16,7 +16,7 @@ Files in this directory are **read from disk by the seeder** (or copied into `pa
## Contents ## Contents
- **`init.js`** — Kernel entry: must define `async function start(ctx)`. Boot order: **`/etc/os-release`** → **`/etc/motd`** → optional **`/etc/bare-os/rc.profile.<profile>`** (profile from **`BARE_OS_BOOT_PROFILE`** or first line of **`/etc/bare-os/profile`**; the booter mirrors the resolved name in **`ctx.env.BARE_OS_BOOT_PROFILE_RESOLVED`** and **`/run/bare-os/boot_profile`**) → **`/etc/bare-os/rc`** → **`/etc/bare-os/rc.d/*`** (sorted; digit-prefixed names only; skip dotfiles, `*~`, `README*`, `*.md`; optional **`BARE_OS_RC_D_SKIP`** comma list and **`prefix*`** patterns) → optional **`/etc/bare-os/rc.local`** → **`/etc/bare-os/kernel.d/*`** (same rules as **`rc.d`**) → banner → when **`BARE_OS_SKIP_REPL`**, optional **onboot** lines from **`BARE_OS_ONBOOT`** or **`/etc/bare-os/onboot`** → **`readLine` / `execLine`** loop. Boot **`execLine`** errors in trusted snippets are logged; with **`BARE_OS_BOOT_STRICT=1`** or **`true`**, the first throw calls **`requestBooterExit(1)`** and stops later boot phases. Custom kernels may call **`ctx.registerKernelShutdownHook(fn)`** before initd disposers; use **`ctx.bareOsRuntimeCaps`** for limits, pseudo paths, and **`features`** ([`developer-guide/02-the-context-object.md`](../developer-guide/02-the-context-object.md)). - **`init.js`** — Kernel entry: must define `async function start(ctx)`. Boot order: **`/etc/os-release`** → **`/etc/motd`** → optional **`/etc/bare-os/rc.profile.<profile>`** (profile from **`BARE_OS_BOOT_PROFILE`** or first line of **`/etc/bare-os/profile`**; the booter mirrors the resolved name in **`ctx.env.BARE_OS_BOOT_PROFILE_RESOLVED`** and **`/run/bare-os/boot_profile`**) → **`/etc/bare-os/rc`** → **`/etc/bare-os/rc.d/*`** (sorted; digit-prefixed names only; skip dotfiles, `*~`, `README*`, `*.md`; optional **`BARE_OS_RC_D_SKIP`** comma list and **`prefix*`** patterns) → optional **`/etc/bare-os/rc.local`** → **`/etc/bare-os/kernel.d/*`** (same rules as **`rc.d`**) → banner → when **`BARE_OS_SKIP_REPL`**, optional **onboot** lines from **`BARE_OS_ONBOOT`** or **`/etc/bare-os/onboot`** → **`readLine` / `execLine`** loop. Boot **`execLine`** errors in trusted snippets are logged; with **`BARE_OS_BOOT_STRICT=1`** or **`true`**, the first throw calls **`requestBooterExit(1)`** and stops later boot phases. Custom kernels may call **`ctx.registerKernelShutdownHook(fn)`** before initd disposers; use **`ctx.bareOsRuntimeCaps`** for limits, pseudo paths, and **`features`** ([`developer-guide/02-the-context-object.md`](../developer-guide/02-the-context-object.md)).
- **`bin/`** — **Tier-1 utilities** built by [bare-os-coreutils](../packages/bare-os-coreutils/README.md). Each file is **`runtime.js`** + optional **`lib/*-engine.js`** (**`sed`**, **`awk`**) or **`lib/man-render.js`** (**`man`**) + **`async function run(ctx, argv)`** (no ESM **`import`** in **`src/`**). - **`bin/`** — **Tier-1 utilities** built by [bare-os-coreutils](../packages/bare-os-coreutils/README.md). Each file is **`runtime.js`** + optional **`lib/*-engine.js`** (**`sed`**, **`awk`**), **`lib/man-render.js`** (**`man`**), **`lib/edit-*.js`** (**`edit`** / **`nano`** — same **`src/edit.js`**), lscolors chunks (**`ls`**, **`dircolors`**), etc. + **`async function run(ctx, argv)`** (no ESM **`import`** in **`src/`**). **`/bin/nano`** duplicates **`/bin/edit`** for familiarity; the shells default **`nano``edit`** alias uses the **`edit`** command name after expansion.
- **`lib/bare/`** — Optional IIFE bundles + **`manifest.json`** for **`ctx.bare`** drive merge, built by [bare-os-bare-libs](../packages/bare-os-bare-libs/README.md). Same trust model as **`bin/`** (trusted seeded image). - **`lib/bare/`** — Optional IIFE bundles + **`manifest.json`** for **`ctx.bare`** drive merge, built by [bare-os-bare-libs](../packages/bare-os-bare-libs/README.md). Same trust model as **`bin/`** (trusted seeded image).
- **`share/man/man.json`** — Merged manual database for **`/bin/man`** (built by **`bare-os-coreutils`**; see [handbook ch.10](../handbook/10-manpages-and-online-help.md)). - **`share/man/man.json`** — Merged manual database for **`/bin/man`** (built by **`bare-os-coreutils`**; see [handbook ch.10](../handbook/10-manpages-and-online-help.md)).
- **`etc/os-release`** — Static OS metadata (`NAME`, `VERSION`, …). - **`etc/os-release`** — Static OS metadata (`NAME`, `VERSION`, …).
+1323
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -62,7 +62,7 @@ function barePosixBlocks(size) {
async function run(ctx, argv) { async function run(ctx, argv) {
ctx.console.log( ctx.console.log(
'Bare OS — default user: guest | shell builtins: alias, barerc, cd, command, export, exit, login, logout, readonly, type, umask, unalias, unset, : | /bin: awk basename cat chgrp chmod chown cksum clear cp crontab curl cut date dirname dircolors du echo env exit false find getconf grep head hdms help hostname id journalctl jq ln login logout logname ls man mkdir mkfifo mv nl od pathchk printenv printf pwd readlink rm rmdir savevault sed seq sleep sort stat systemctl tail tee test theme time touch tr true tty uname wc wget which whoami xargs' 'Bare OS — default user: guest | shell builtins: alias, barerc, cd, command, export, exit, login, logout, readonly, type, umask, unalias, unset, : | /bin: awk basename cat chgrp chmod chown cksum clear cp crontab curl cut date dirname dircolors du edit echo env exit false find getconf grep head hdms help hostname id journalctl jq ln login logout logname ls man mkdir mkfifo mktemp nano mv nl od pathchk printenv printf pwd readlink rm rmdir savevault sed seq sleep sort stat systemctl tail tee test theme time touch tr true tty uname wc wget which whoami xargs'
) )
ctx.console.log( ctx.console.log(
'Docs: man <command> | man handbook (full handbook, section 7) | man bare-os-shell | man -k <word> | man -l' 'Docs: man <command> | man handbook (full handbook, section 7) | man bare-os-shell | man -k <word> | man -l'
+1323
View File
File diff suppressed because it is too large Load Diff
+188 -188
View File
@@ -7,18 +7,18 @@
"b4a" "b4a"
] ]
}, },
{
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
"keys": [
"hypercoreIdEncoding"
]
},
{ {
"path": "/lib/bare/bundles/safetyCatch.js", "path": "/lib/bare/bundles/safetyCatch.js",
"keys": [ "keys": [
"safetyCatch" "safetyCatch"
] ]
}, },
{
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
"keys": [
"hypercoreIdEncoding"
]
},
{ {
"path": "/lib/bare/bundles/compactEncoding.js", "path": "/lib/bare/bundles/compactEncoding.js",
"keys": [ "keys": [
@@ -37,18 +37,18 @@
"protomux" "protomux"
] ]
}, },
{
"path": "/lib/bare/bundles/bareEvents.js",
"keys": [
"bareEvents"
]
},
{ {
"path": "/lib/bare/bundles/bareEncoding.js", "path": "/lib/bare/bundles/bareEncoding.js",
"keys": [ "keys": [
"bareEncoding" "bareEncoding"
] ]
}, },
{
"path": "/lib/bare/bundles/bareEvents.js",
"keys": [
"bareEvents"
]
},
{ {
"path": "/lib/bare/bundles/barePath.js", "path": "/lib/bare/bundles/barePath.js",
"keys": [ "keys": [
@@ -67,6 +67,18 @@
"bareAbortController" "bareAbortController"
] ]
}, },
{
"path": "/lib/bare/bundles/bareAnsiEscapes.js",
"keys": [
"bareAnsiEscapes"
]
},
{
"path": "/lib/bare/bundles/bareAddonResolve.js",
"keys": [
"bareAddonResolve"
]
},
{ {
"path": "/lib/bare/bundles/bareReadline.js", "path": "/lib/bare/bundles/bareReadline.js",
"keys": [ "keys": [
@@ -74,9 +86,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareAnsiEscapes.js", "path": "/lib/bare/bundles/bareAppKit.js",
"keys": [ "keys": [
"bareAnsiEscapes" "bareAppKit"
] ]
}, },
{ {
@@ -86,9 +98,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareAddonResolve.js", "path": "/lib/bare/bundles/bareApk.js",
"keys": [ "keys": [
"bareAddonResolve" "bareApk"
]
},
{
"path": "/lib/bare/bundles/bareAssert.js",
"keys": [
"bareAssert"
] ]
}, },
{ {
@@ -109,24 +127,6 @@
"fetch" "fetch"
] ]
}, },
{
"path": "/lib/bare/bundles/bareApk.js",
"keys": [
"bareApk"
]
},
{
"path": "/lib/bare/bundles/bareAssert.js",
"keys": [
"bareAssert"
]
},
{
"path": "/lib/bare/bundles/bareAppKit.js",
"keys": [
"bareAppKit"
]
},
{ {
"path": "/lib/bare/bundles/bareBmp.js", "path": "/lib/bare/bundles/bareBmp.js",
"keys": [ "keys": [
@@ -151,12 +151,6 @@
"bareBuffer" "bareBuffer"
] ]
}, },
{
"path": "/lib/bare/bundles/bareBundleEvaluate.js",
"keys": [
"bareBundleEvaluate"
]
},
{ {
"path": "/lib/bare/bundles/bareBluetoothApple.js", "path": "/lib/bare/bundles/bareBluetoothApple.js",
"keys": [ "keys": [
@@ -164,9 +158,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareBoot.js", "path": "/lib/bare/bundles/bareBundleEvaluate.js",
"keys": [ "keys": [
"bareBoot" "bareBundleEvaluate"
] ]
}, },
{ {
@@ -175,18 +169,18 @@
"bareConsole" "bareConsole"
] ]
}, },
{
"path": "/lib/bare/bundles/bareDebugLog.js",
"keys": [
"bareDebugLog"
]
},
{ {
"path": "/lib/bare/bundles/bareBundleId.js", "path": "/lib/bare/bundles/bareBundleId.js",
"keys": [ "keys": [
"bareBundleId" "bareBundleId"
] ]
}, },
{
"path": "/lib/bare/bundles/bareBoot.js",
"keys": [
"bareBoot"
]
},
{ {
"path": "/lib/bare/bundles/bareDaemon.js", "path": "/lib/bare/bundles/bareDaemon.js",
"keys": [ "keys": [
@@ -194,9 +188,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareDelta.js", "path": "/lib/bare/bundles/bareDebugLog.js",
"keys": [ "keys": [
"bareDelta" "bareDebugLog"
] ]
}, },
{ {
@@ -206,9 +200,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareDns.js", "path": "/lib/bare/bundles/bareDelta.js",
"keys": [ "keys": [
"bareDns" "bareDelta"
] ]
}, },
{ {
@@ -218,15 +212,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareEnv.js", "path": "/lib/bare/bundles/bareDns.js",
"keys": [ "keys": [
"bareEnv" "bareDns"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareExif.js", "path": "/lib/bare/bundles/bareEnv.js",
"keys": [ "keys": [
"bareExif" "bareEnv"
] ]
}, },
{ {
@@ -236,15 +230,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareDgram.js", "path": "/lib/bare/bundles/bareExif.js",
"keys": [ "keys": [
"bareDgram" "bareExif"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareFfmpegEncodings.js", "path": "/lib/bare/bundles/bareDgram.js",
"keys": [ "keys": [
"bareFfmpegEncodings" "bareDgram"
] ]
}, },
{ {
@@ -253,12 +247,24 @@
"bareFfmpeg" "bareFfmpeg"
] ]
}, },
{
"path": "/lib/bare/bundles/bareFfmpegEncodings.js",
"keys": [
"bareFfmpegEncodings"
]
},
{ {
"path": "/lib/bare/bundles/bareFormData.js", "path": "/lib/bare/bundles/bareFormData.js",
"keys": [ "keys": [
"bareFormData" "bareFormData"
] ]
}, },
{
"path": "/lib/bare/bundles/bareFileLogger.js",
"keys": [
"bareFileLogger"
]
},
{ {
"path": "/lib/bare/bundles/bareFormat.js", "path": "/lib/bare/bundles/bareFormat.js",
"keys": [ "keys": [
@@ -271,18 +277,6 @@
"bareGif" "bareGif"
] ]
}, },
{
"path": "/lib/bare/bundles/bareFileLogger.js",
"keys": [
"bareFileLogger"
]
},
{
"path": "/lib/bare/bundles/bareHeif.js",
"keys": [
"bareHeif"
]
},
{ {
"path": "/lib/bare/bundles/bareHrtime.js", "path": "/lib/bare/bundles/bareHrtime.js",
"keys": [ "keys": [
@@ -295,24 +289,18 @@
"bareGtk" "bareGtk"
] ]
}, },
{
"path": "/lib/bare/bundles/bareHeif.js",
"keys": [
"bareHeif"
]
},
{ {
"path": "/lib/bare/bundles/bareHttpParser.js", "path": "/lib/bare/bundles/bareHttpParser.js",
"keys": [ "keys": [
"bareHttpParser" "bareHttpParser"
] ]
}, },
{
"path": "/lib/bare/bundles/bareIco.js",
"keys": [
"bareIco"
]
},
{
"path": "/lib/bare/bundles/bareImageResample.js",
"keys": [
"bareImageResample"
]
},
{ {
"path": "/lib/bare/bundles/bareFs.js", "path": "/lib/bare/bundles/bareFs.js",
"keys": [ "keys": [
@@ -320,9 +308,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareHttp1.js", "path": "/lib/bare/bundles/bareImageResample.js",
"keys": [ "keys": [
"bareHttp1" "bareImageResample"
]
},
{
"path": "/lib/bare/bundles/bareIco.js",
"keys": [
"bareIco"
] ]
}, },
{ {
@@ -331,6 +325,12 @@
"bareInspect" "bareInspect"
] ]
}, },
{
"path": "/lib/bare/bundles/bareHttp1.js",
"keys": [
"bareHttp1"
]
},
{ {
"path": "/lib/bare/bundles/bareHttps.js", "path": "/lib/bare/bundles/bareHttps.js",
"keys": [ "keys": [
@@ -343,24 +343,12 @@
"bareJpeg" "bareJpeg"
] ]
}, },
{
"path": "/lib/bare/bundles/bareIpc.js",
"keys": [
"bareIpc"
]
},
{ {
"path": "/lib/bare/bundles/bareIntl.js", "path": "/lib/bare/bundles/bareIntl.js",
"keys": [ "keys": [
"bareIntl" "bareIntl"
] ]
}, },
{
"path": "/lib/bare/bundles/bareLief.js",
"keys": [
"bareLief"
]
},
{ {
"path": "/lib/bare/bundles/bareLogger.js", "path": "/lib/bare/bundles/bareLogger.js",
"keys": [ "keys": [
@@ -368,9 +356,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareInspector.js", "path": "/lib/bare/bundles/bareIpc.js",
"keys": [ "keys": [
"bareInspector" "bareIpc"
]
},
{
"path": "/lib/bare/bundles/bareLief.js",
"keys": [
"bareLief"
] ]
}, },
{ {
@@ -380,15 +374,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareMake.js", "path": "/lib/bare/bundles/bareInspector.js",
"keys": [ "keys": [
"bareMake" "bareInspector"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareModuleResolve.js", "path": "/lib/bare/bundles/bareMake.js",
"keys": [ "keys": [
"bareModuleResolve" "bareMake"
] ]
}, },
{ {
@@ -398,9 +392,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareModuleTraverse.js", "path": "/lib/bare/bundles/bareModuleResolve.js",
"keys": [ "keys": [
"bareModuleTraverse" "bareModuleResolve"
] ]
}, },
{ {
@@ -410,9 +404,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareNative.js", "path": "/lib/bare/bundles/bareModuleTraverse.js",
"keys": [ "keys": [
"bareNative" "bareModuleTraverse"
] ]
}, },
{ {
@@ -428,9 +422,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareMedia.js", "path": "/lib/bare/bundles/bareNative.js",
"keys": [ "keys": [
"bareMedia" "bareNative"
] ]
}, },
{ {
@@ -445,48 +439,36 @@
"bareOs" "bareOs"
] ]
}, },
{
"path": "/lib/bare/bundles/bareMedia.js",
"keys": [
"bareMedia"
]
},
{ {
"path": "/lib/bare/bundles/bareNet.js", "path": "/lib/bare/bundles/bareNet.js",
"keys": [ "keys": [
"bareNet" "bareNet"
] ]
}, },
{
"path": "/lib/bare/bundles/bareDev.js",
"keys": [
"bareDev"
]
},
{
"path": "/lib/bare/bundles/barePack.js",
"keys": [
"barePack"
]
},
{ {
"path": "/lib/bare/bundles/barePerformance.js", "path": "/lib/bare/bundles/barePerformance.js",
"keys": [ "keys": [
"barePerformance" "barePerformance"
] ]
}, },
{
"path": "/lib/bare/bundles/barePack.js",
"keys": [
"barePack"
]
},
{ {
"path": "/lib/bare/bundles/barePackDrive.js", "path": "/lib/bare/bundles/barePackDrive.js",
"keys": [ "keys": [
"barePackDrive" "barePackDrive"
] ]
}, },
{
"path": "/lib/bare/bundles/barePng.js",
"keys": [
"barePng"
]
},
{
"path": "/lib/bare/bundles/bareNodeRuntime.js",
"keys": [
"bareNodeRuntime"
]
},
{ {
"path": "/lib/bare/bundles/barePipe.js", "path": "/lib/bare/bundles/barePipe.js",
"keys": [ "keys": [
@@ -494,9 +476,21 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/barePrebuild.js", "path": "/lib/bare/bundles/barePng.js",
"keys": [ "keys": [
"barePrebuild" "barePng"
]
},
{
"path": "/lib/bare/bundles/bareDev.js",
"keys": [
"bareDev"
]
},
{
"path": "/lib/bare/bundles/bareNodeRuntime.js",
"keys": [
"bareNodeRuntime"
] ]
}, },
{ {
@@ -506,15 +500,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareQuerystring.js", "path": "/lib/bare/bundles/barePrebuild.js",
"keys": [ "keys": [
"bareQuerystring" "barePrebuild"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareProcess.js", "path": "/lib/bare/bundles/bareQuerystring.js",
"keys": [ "keys": [
"bareProcess" "bareQuerystring"
] ]
}, },
{ {
@@ -529,12 +523,24 @@
"bareRealm" "bareRealm"
] ]
}, },
{
"path": "/lib/bare/bundles/bareProcess.js",
"keys": [
"bareProcess"
]
},
{ {
"path": "/lib/bare/bundles/bareRuntime.js", "path": "/lib/bare/bundles/bareRuntime.js",
"keys": [ "keys": [
"bareRuntime" "bareRuntime"
] ]
}, },
{
"path": "/lib/bare/bundles/bareRpc.js",
"keys": [
"bareRpc"
]
},
{ {
"path": "/lib/bare/bundles/barePromClient.js", "path": "/lib/bare/bundles/barePromClient.js",
"keys": [ "keys": [
@@ -559,12 +565,6 @@
"bareRepl" "bareRepl"
] ]
}, },
{
"path": "/lib/bare/bundles/bareRpc.js",
"keys": [
"bareRpc"
]
},
{ {
"path": "/lib/bare/bundles/bareRun.js", "path": "/lib/bare/bundles/bareRun.js",
"keys": [ "keys": [
@@ -577,12 +577,6 @@
"bareSignals" "bareSignals"
] ]
}, },
{
"path": "/lib/bare/bundles/bareStorage.js",
"keys": [
"bareStorage"
]
},
{ {
"path": "/lib/bare/bundles/bareSidecar.js", "path": "/lib/bare/bundles/bareSidecar.js",
"keys": [ "keys": [
@@ -596,15 +590,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareStream.js", "path": "/lib/bare/bundles/bareStorage.js",
"keys": [ "keys": [
"bareStream" "bareStorage"
]
},
{
"path": "/lib/bare/bundles/bareStdio.js",
"keys": [
"bareStdio"
] ]
}, },
{ {
@@ -614,9 +602,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareTap.js", "path": "/lib/bare/bundles/bareStream.js",
"keys": [ "keys": [
"bareTap" "bareStream"
]
},
{
"path": "/lib/bare/bundles/bareStdio.js",
"keys": [
"bareStdio"
] ]
}, },
{ {
@@ -632,9 +626,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareSubprocess.js", "path": "/lib/bare/bundles/bareTap.js",
"keys": [ "keys": [
"bareSubprocess" "bareTap"
] ]
}, },
{ {
@@ -644,9 +638,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareTimers.js", "path": "/lib/bare/bundles/bareSubprocess.js",
"keys": [ "keys": [
"bareTimers" "bareSubprocess"
] ]
}, },
{ {
@@ -655,18 +649,24 @@
"bareThread" "bareThread"
] ]
}, },
{
"path": "/lib/bare/bundles/bareTcp.js",
"keys": [
"bareTcp"
]
},
{ {
"path": "/lib/bare/bundles/bareTpl.js", "path": "/lib/bare/bundles/bareTpl.js",
"keys": [ "keys": [
"bareTpl" "bareTpl"
] ]
}, },
{
"path": "/lib/bare/bundles/bareTimers.js",
"keys": [
"bareTimers"
]
},
{
"path": "/lib/bare/bundles/bareTcp.js",
"keys": [
"bareTcp"
]
},
{ {
"path": "/lib/bare/bundles/bareType.js", "path": "/lib/bare/bundles/bareType.js",
"keys": [ "keys": [
@@ -703,18 +703,18 @@
"bareV8" "bareV8"
] ]
}, },
{
"path": "/lib/bare/bundles/bareWalkHandles.js",
"keys": [
"bareWalkHandles"
]
},
{ {
"path": "/lib/bare/bundles/bareVm.js", "path": "/lib/bare/bundles/bareVm.js",
"keys": [ "keys": [
"bareVm" "bareVm"
] ]
}, },
{
"path": "/lib/bare/bundles/bareWalkHandles.js",
"keys": [
"bareWalkHandles"
]
},
{ {
"path": "/lib/bare/bundles/bareUnionBundle.js", "path": "/lib/bare/bundles/bareUnionBundle.js",
"keys": [ "keys": [
@@ -746,9 +746,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareWinUi.js", "path": "/lib/bare/bundles/bareUtils.js",
"keys": [ "keys": [
"bareWinUi" "bareUtils"
] ]
}, },
{ {
@@ -758,15 +758,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareXdiff.js", "path": "/lib/bare/bundles/bareWinUi.js",
"keys": [ "keys": [
"bareXdiff" "bareWinUi"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareUtils.js", "path": "/lib/bare/bundles/bareXdiff.js",
"keys": [ "keys": [
"bareUtils" "bareXdiff"
] ]
}, },
{ {
@@ -776,9 +776,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareWorker.js", "path": "/lib/bare/bundles/bareZmq.js",
"keys": [ "keys": [
"bareWorker" "bareZmq"
] ]
}, },
{ {
@@ -788,9 +788,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareZmq.js", "path": "/lib/bare/bundles/bareWorker.js",
"keys": [ "keys": [
"bareZmq" "bareWorker"
] ]
} }
], ],
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -26,7 +26,7 @@ node index.js
| `swarm-disk.js` | Peer mux, MBR/read RPC, replication hooks | | `swarm-disk.js` | Peer mux, MBR/read RPC, replication hooks |
| `kernel-runner.js` | `runKernelFromSource`, `runBinCommand`, `resolveBinInPath` (PATH on system drive); `git` / `curl` / `wget` delegates | | `kernel-runner.js` | `runKernelFromSource`, `runBinCommand`, `resolveBinInPath` (PATH on system drive); `git` / `curl` / `wget` delegates |
| `vfs.js` | Two-drive routing; `mkdir`/`rmdir` (`.bareos_empty`), `chmod`, `symlink`, pseudo `/proc`/`/sys`, `watch`, … | | `vfs.js` | Two-drive routing; `mkdir`/`rmdir` (`.bareos_empty`), `chmod`, `symlink`, pseudo `/proc`/`/sys`, `watch`, … |
| `shell.js` | Tokenize, pipelines, redirections, builtins (`barerc`, `unset`, `readonly`, `umask`, `command`, `type`, …), `execShellLine`, `loadBarerc` | | `shell.js` | Tokenize, pipelines, redirections, builtins (`barerc`, `unset`, `readonly`, `umask`, `command`, `type`, …), `execShellLine`, `loadBarerc`; default aliases include **`nano``edit`** (TTY editor) |
| `bare-os-theme-presets.js` | Named themes (`BARE_OS_THEME`), `applyBareOsThemeFromEnv`, `BARE_OS_COLOR_DEPTH` downgrades for REPL colors | | `bare-os-theme-presets.js` | Named themes (`BARE_OS_THEME`), `applyBareOsThemeFromEnv`, `BARE_OS_COLOR_DEPTH` downgrades for REPL colors |
| `bare-os-ipc.js` | FIFOs, JSON-RPC (`pushJson`/`takeJson`, token + line limits), fan-out pub/sub, `stats` | | `bare-os-ipc.js` | FIFOs, JSON-RPC (`pushJson`/`takeJson`, token + line limits), fan-out pub/sub, `stats` |
| `bare-os-abort.js` | `raceWithAbortAndTimeout` for `execLine` / `readLine` / VFS / `runBinCommand` | | `bare-os-abort.js` | `raceWithAbortAndTimeout` for `execLine` / `readLine` / VFS / `runBinCommand` |
+6 -3
View File
@@ -88,18 +88,21 @@ export async function runKernelFromSource(source, ctx) {
} }
/** /**
* Evaluate user script source: top-level statements run in an async function with `ctx` and `argv`.
* If the script defines a top-level `run` function, it is awaited after the body (same as `/bin` utilities).
*
* @param {Record<string, unknown>} ctx * @param {Record<string, unknown>} ctx
* @param {string} src * @param {string} src
* @param {string[]} argv * @param {string[]} argv
* @param {string} [label] * @param {string} [_label] reserved for diagnostics
*/ */
async function runScriptFromSource(ctx, src, argv, label = argv[0]) { async function runScriptFromSource(ctx, src, argv, _label = argv[0]) {
try { try {
const body = stripShebang(src) const body = stripShebang(src)
const fn = new AsyncFunction( const fn = new AsyncFunction(
'ctx', 'ctx',
'argv', 'argv',
`${body}\nif (typeof run !== 'function') throw new Error('missing run() in ${label}')\nreturn run(ctx, argv)\n` `${body}\nif (typeof run === 'function') await run(ctx, argv)\n`
) )
await fn(ctx, argv) await fn(ctx, argv)
if (ctx.exitCode === undefined || ctx.exitCode === null) ctx.exitCode = 0 if (ctx.exitCode === undefined || ctx.exitCode === null) ctx.exitCode = 0
+1
View File
@@ -95,6 +95,7 @@ export function getBareOsPipelineLimits(env) {
*/ */
export function defaultShellAliases() { export function defaultShellAliases() {
return { return {
nano: 'edit',
ll: 'ls -la', ll: 'ls -la',
la: 'ls -A', la: 'ls -A',
l: 'ls', l: 'ls',
+119
View File
@@ -433,6 +433,49 @@ async function run(ctx) { ctx.out.push('ok') }
rmSync(dir, { recursive: true, force: true }) rmSync(dir, { recursive: true, force: true })
}) })
test('runBinCommand runs top-level user script without run()', async (t) => {
const dir = testCorestoreDir('norun')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pnr'))
await drive.ready()
await personal.ready()
await personal.put(
personalHomeBacking('/home/user', 'plain.js'),
b4a.from(`ctx.out.push('only-top')`)
)
const out = []
const ctx = testCtx(drive, personal)
ctx.out = out
await runBinCommand(ctx, ['./plain.js'])
t.is(out[0], 'only-top')
t.is(ctx.exitCode, 0)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('runBinCommand runs top-level then run() when both present', async (t) => {
const dir = testCorestoreDir('bothrun')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pbr'))
await drive.ready()
await personal.ready()
await personal.put(
personalHomeBacking('/home/user', 'hybrid.js'),
b4a.from(`ctx.out.push('first')
async function run(ctx) { ctx.out.push('second') }
`)
)
const out = []
const ctx = testCtx(drive, personal)
ctx.out = out
await runBinCommand(ctx, ['./hybrid.js'])
t.alike(out, ['first', 'second'])
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('runBinCommand user script error is caught and logged', async (t) => { test('runBinCommand user script error is caught and logged', async (t) => {
const dir = testCorestoreDir('throwjs') const dir = testCorestoreDir('throwjs')
const store = new Corestore(dir) const store = new Corestore(dir)
@@ -1228,6 +1271,11 @@ test('defaultShellAliases does not remap sed', async (t) => {
t.is(defaultShellAliases().sed, undefined) t.is(defaultShellAliases().sed, undefined)
}) })
test('defaultShellAliases nano maps to edit', async (t) => {
t.is(defaultShellAliases().nano, 'edit')
t.alike(expandArgvAliases(['nano', 'x'], defaultShellAliases()), ['edit', 'x'])
})
test('expandArgvAliases throws on cyclic alias chain', async (t) => { test('expandArgvAliases throws on cyclic alias chain', async (t) => {
const cyclic = { a: 'b', b: 'a' } const cyclic = { a: 'b', b: 'a' }
t.exception( t.exception(
@@ -1439,6 +1487,77 @@ test('runBinCommand dircolors -p prints database', async (t) => {
rmSync(dir, { recursive: true, force: true }) rmSync(dir, { recursive: true, force: true })
}) })
test('runBinCommand edit --help prints usage', async (t) => {
const editPath = path.join(__dirname, '../../kernel/bin/edit')
const editSrc = await readFile(editPath, 'utf8')
const dir = testCorestoreDir('edithlp')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('edh'))
await drive.ready()
await personal.ready()
await drive.put('/bin/edit', b4a.from(editSrc))
const lines = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (s) => lines.push(String(s)),
error: (...a) => lines.push(a.join(' '))
}
await runBinCommand(ctx, ['edit', '--help'])
const out = lines.join('\n')
t.ok(out.includes('usage:'))
t.ok(out.includes('TTY'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('runBinCommand edit requires TTY', async (t) => {
const editPath = path.join(__dirname, '../../kernel/bin/edit')
const editSrc = await readFile(editPath, 'utf8')
const dir = testCorestoreDir('ednotty')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('ednt'))
await drive.ready()
await personal.ready()
await drive.put('/bin/edit', b4a.from(editSrc))
const lines = []
const ctx = testCtx(drive, personal)
ctx.replStdin = { isTTY: false }
ctx.console = {
log: (s) => lines.push(String(s)),
error: (...a) => lines.push(a.join(' '))
}
await runBinCommand(ctx, ['edit', 'x.txt'])
t.is(ctx.exitCode, 1)
t.ok(lines.some((l) => l.includes('TTY')))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('runBinCommand nano --help matches edit bundle', async (t) => {
const nanoPath = path.join(__dirname, '../../kernel/bin/nano')
const nanoSrc = await readFile(nanoPath, 'utf8')
const dir = testCorestoreDir('nanohlp')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('nanh'))
await drive.ready()
await personal.ready()
await drive.put('/bin/nano', b4a.from(nanoSrc))
const lines = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (s) => lines.push(String(s)),
error: (...a) => lines.push(a.join(' '))
}
await runBinCommand(ctx, ['nano', '-h'])
const out = lines.join('\n')
t.ok(out.includes('usage:'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('tokenize leaves echo 2 > file as stdout redirect not 2>', async (t) => { test('tokenize leaves echo 2 > file as stdout redirect not 2>', async (t) => {
const toks = tokenize('echo 2 > /tmp/x') const toks = tokenize('echo 2 > /tmp/x')
const words = toks.filter((x) => x.type === 'word').map((x) => x.value) const words = toks.filter((x) => x.type === 'word').map((x) => x.value)
+3 -1
View File
@@ -33,7 +33,9 @@ Or `node packages/bare-os-coreutils/build.mjs`.
**Source of truth:** **`lib/commands.mjs`** — **`COREUTILS_COMMANDS`** (imported by **`build.mjs`** and **`scripts/build-man-db.mjs`**). Each name must have **`man/pages/<name>.json`**. **Source of truth:** **`lib/commands.mjs`** — **`COREUTILS_COMMANDS`** (imported by **`build.mjs`** and **`scripts/build-man-db.mjs`**). Each name must have **`man/pages/<name>.json`**.
`awk`, `basename`, `cat`, `chgrp`, `chmod`, `chown`, `cksum`, `clear`, `cp`, `crontab`, `cut`, `date`, `dirname`, `dircolors`, `du`, `echo`, `env`, `exit`, `false`, `find`, `getconf`, `git-pear`, `grep`, `head`, `hdms`, `help`, `hostname`, `id`, `jq`, `ln`, `login`, `logout`, `logname`, `ls`, `man`, `mkdir`, `mkfifo`, `mktemp`, `mv`, `nl`, `od`, `pathchk`, `printenv`, `printf`, `pwd`, `readlink`, `rm`, `rmdir`, `savevault`, `sed`, `seq`, `sleep`, `sort`, `stat`, `tail`, `tee`, `test`, `theme`, `time`, `touch`, `tr`, `true`, `tty`, `uname`, `wc`, `which`, `whoami`, `xargs` `awk`, `basename`, `cat`, `chgrp`, `chmod`, `chown`, `cksum`, `clear`, `cp`, `crontab`, `cut`, `date`, `dirname`, `dircolors`, `du`, `edit`, `echo`, `env`, `exit`, `false`, `find`, `getconf`, `git-pear`, `grep`, `head`, `hdms`, `help`, `hostname`, `id`, `jq`, `ln`, `login`, `logout`, `logname`, `ls`, `man`, `mkdir`, `mkfifo`, `mktemp`, `mv`, `nano`, `nl`, `od`, `pathchk`, `printenv`, `printf`, `pwd`, `readlink`, `rm`, `rmdir`, `savevault`, `sed`, `seq`, `sleep`, `sort`, `stat`, `tail`, `tee`, `test`, `theme`, `time`, `touch`, `tr`, `true`, `tty`, `uname`, `wc`, `which`, `whoami`, `xargs`
**`edit`** is a full-screen TTY buffer editor (syntax highlighting, search, save). **`nano`** is built from the same **`src/edit.js`** with the same **`lib/edit-*.js`** preamble; **`/bin/nano`** exists for familiarity, and the stock shell alias **`nano``edit`** routes **`nano`** to that utility (see **`packages/bare-os-booter/lib/shell.js`**). Both require a real TTY (**`stdout.isTTY`**).
**`ls`** prepends **[`bare-os-lscolors`](../bare-os-lscolors/bare-os-lscolors.js)** for **`LS_COLORS`** / dircolors parsing. **`dircolors`** and **`theme`** integrate with the booters **`bare-os-theme-presets.js`** (see [docs/themes/README.md](../../docs/themes/README.md)). **`ls`** prepends **[`bare-os-lscolors`](../bare-os-lscolors/bare-os-lscolors.js)** for **`LS_COLORS`** / dircolors parsing. **`dircolors`** and **`theme`** integrate with the booters **`bare-os-theme-presets.js`** (see [docs/themes/README.md](../../docs/themes/README.md)).
+24 -3
View File
@@ -17,7 +17,27 @@ const preamble = {
jq: ['jq-engine.js'], jq: ['jq-engine.js'],
man: ['man-render.js'], man: ['man-render.js'],
ls: ['bare-os-lscolors.js', 'ls-colors.js'], ls: ['bare-os-lscolors.js', 'ls-colors.js'],
dircolors: ['bare-os-lscolors.js'] dircolors: ['bare-os-lscolors.js'],
edit: [
'edit-ansi.js',
'edit-highlight.js',
'edit-buffer.js',
'edit-key-parse.js',
'edit-tui.js'
],
nano: [
'edit-ansi.js',
'edit-highlight.js',
'edit-buffer.js',
'edit-key-parse.js',
'edit-tui.js'
]
}
/** @param {string} binName */
function commandSourceFile(binName) {
if (binName === 'nano') return 'edit'
return binName
} }
const commands = COREUTILS_COMMANDS const commands = COREUTILS_COMMANDS
@@ -36,7 +56,7 @@ export async function build() {
for (const name of commands) { for (const name of commands) {
let pre = '' let pre = ''
const extras = preamble[name] const extras = preamble[name] || preamble[commandSourceFile(name)]
if (extras) { if (extras) {
for (const f of extras) { for (const f of extras) {
const chunkPath = const chunkPath =
@@ -48,7 +68,8 @@ export async function build() {
pre += chunk + '\n' pre += chunk + '\n'
} }
} }
const body = await readFile(join(__dirname, 'src', `${name}.js`), 'utf8') const srcName = commandSourceFile(name)
const body = await readFile(join(__dirname, 'src', `${srcName}.js`), 'utf8')
const out = runtime + '\n' + pre + body const out = runtime + '\n' + pre + body
await writeFile(join(kernelBin, name), out) await writeFile(join(kernelBin, name), out)
await writeFile(join(seederKernelBin, name), out) await writeFile(join(seederKernelBin, name), out)
@@ -18,6 +18,7 @@ export const COREUTILS_COMMANDS = [
'dirname', 'dirname',
'dircolors', 'dircolors',
'du', 'du',
'edit',
'echo', 'echo',
'env', 'env',
'exit', 'exit',
@@ -42,6 +43,7 @@ export const COREUTILS_COMMANDS = [
'mkfifo', 'mkfifo',
'mktemp', 'mktemp',
'mv', 'mv',
'nano',
'nl', 'nl',
'od', 'od',
'pathchk', 'pathchk',
@@ -0,0 +1,98 @@
/** ANSI helpers for /bin/edit (preamble; no import in src). */
const EDIT_ANSI_RESET = '\x1b[0m'
/**
* @param {Record<string, unknown>} [ctx]
* @returns {import('stream').Writable | undefined}
*/
function bareEditResolveStdout(ctx) {
if (!ctx || typeof ctx !== 'object') return globalThis.process?.stdout
const c = /** @type {{ replStdout?: unknown, stdout?: unknown }} */ (ctx)
const out = c.replStdout || c.stdout || globalThis.process?.stdout
return /** @type {import('stream').Writable | undefined} */ (out)
}
/**
* @param {Record<string, unknown>} [ctx]
*/
function bareEditUseColor(ctx) {
const env =
ctx && typeof ctx === 'object' && ctx.env && typeof ctx.env === 'object'
? /** @type {Record<string, string>} */ (ctx.env)
: {}
if (env.NO_COLOR != null && String(env.NO_COLOR) !== '') return false
const out = bareEditResolveStdout(ctx)
return Boolean(out && /** @type {{ isTTY?: boolean }} */ (out).isTTY)
}
/**
* @param {'keyword'|'string'|'comment'|'number'|'status'|'inverse'|'dim'} cls
* @param {boolean} on
*/
function bareEditSgr(cls, on) {
if (!on) return ''
switch (cls) {
case 'keyword':
return '\x1b[36m'
case 'string':
return '\x1b[32m'
case 'comment':
return '\x1b[90m'
case 'number':
return '\x1b[33m'
case 'status':
return '\x1b[44m\x1b[97m'
case 'inverse':
return '\x1b[7m'
case 'dim':
return '\x1b[2m'
default:
return ''
}
}
/**
* @param {string} fullLine
* @param {Array<{ start: number, end: number, cls: string }>} spans offsets into fullLine
* @param {number} visStart first column (0-based)
* @param {number} maxLen max code units to show
* @param {boolean} useColor
*/
function bareEditPaintLineWindow(fullLine, spans, visStart, maxLen, useColor) {
const slice = fullLine.slice(visStart, visStart + maxLen)
if (!useColor) return slice
const n = slice.length
const relSpans = spans
.map((s) => ({
start: Math.max(0, s.start - visStart),
end: Math.min(n, s.end - visStart),
cls: s.cls
}))
.filter((s) => s.end > 0 && s.start < n)
.sort((a, b) => a.start - b.start)
let out = ''
let pos = 0
for (const sp of relSpans) {
if (sp.start > pos) out += slice.slice(pos, sp.start)
out +=
bareEditSgr(/** @type {'keyword'} */ (sp.cls), true) +
slice.slice(sp.start, sp.end) +
EDIT_ANSI_RESET
pos = sp.end
}
if (pos < n) out += slice.slice(pos)
return out
}
/**
* Move cursor (1-based row/col, DEC origin). Clamp to sane bounds for escape parsing.
* @param {number} row1
* @param {number} col1
*/
function bareEditCup(row1, col1) {
const r = Math.max(1, Math.min(Math.floor(row1), 9999))
const c = Math.max(1, Math.min(Math.floor(col1), 9999))
return '\x1b[' + r + ';' + c + 'H'
}
@@ -0,0 +1,235 @@
/** Text buffer + cursor + undo for /bin/edit. */
const EDIT_UNDO_MAX = 200
/**
* @param {string} text
*/
function bareEditCreateBuffer(text) {
const lines =
text === '' ? [''] : String(text).replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n')
return {
lines,
row: 0,
col: 0,
dirty: false,
/** @type {{ lines: string[], row: number, col: number }[]} */
undo: [],
/** @type {{ lines: string[], row: number, col: number }[]} */
redo: []
}
}
/**
* @param {{ lines: string[], row: number, col: number, dirty: boolean, undo: unknown[], redo: unknown[] }} buf
*/
function bareEditSnapshot(buf) {
return {
lines: buf.lines.slice(),
row: buf.row,
col: buf.col
}
}
/**
* @param {{ lines: string[], row: number, col: number, dirty: boolean, undo: unknown[], redo: unknown[] }} buf
*/
function bareEditPushUndo(buf) {
buf.undo.push(bareEditSnapshot(buf))
if (buf.undo.length > EDIT_UNDO_MAX) buf.undo.shift()
buf.redo = []
}
/**
* @param {{ lines: string[], row: number, col: number, dirty: boolean, undo: unknown[], redo: unknown[] }} buf
*/
function bareEditUndo(buf) {
const prev = buf.undo.pop()
if (!prev) return false
buf.redo.push(bareEditSnapshot(buf))
buf.lines = /** @type {{ lines: string[] }} */ (prev).lines.slice()
buf.row = prev.row
buf.col = prev.col
buf.dirty = true
return true
}
/**
* @param {{ lines: string[], row: number, col: number, dirty: boolean, undo: unknown[], redo: unknown[] }} buf
*/
function bareEditRedo(buf) {
const next = buf.redo.pop()
if (!next) return false
buf.undo.push(bareEditSnapshot(buf))
buf.lines = /** @type {{ lines: string[] }} */ (next).lines.slice()
buf.row = next.row
buf.col = next.col
buf.dirty = true
return true
}
/**
* @param {{ lines: string[], row: number, col: number, dirty: boolean, undo: unknown[], redo: unknown[] }} buf
*/
function bareEditClampCursor(buf) {
if (buf.row < 0) buf.row = 0
if (buf.row >= buf.lines.length) buf.row = buf.lines.length - 1
const line = buf.lines[buf.row] || ''
if (buf.col < 0) buf.col = 0
if (buf.col > line.length) buf.col = line.length
}
/**
* @param {{ lines: string[], row: number, col: number, dirty: boolean, undo: unknown[], redo: unknown[] }} buf
* @param {string} ch single code unit (MVP)
*/
function bareEditInsertChar(buf, ch) {
bareEditPushUndo(buf)
const line = buf.lines[buf.row]
buf.lines[buf.row] = line.slice(0, buf.col) + ch + line.slice(buf.col)
buf.col += ch.length
buf.dirty = true
}
/**
* @param {{ lines: string[], row: number, col: number, dirty: boolean, undo: unknown[], redo: unknown[] }} buf
*/
function bareEditNewline(buf) {
bareEditPushUndo(buf)
const line = buf.lines[buf.row]
const rest = line.slice(buf.col)
buf.lines[buf.row] = line.slice(0, buf.col)
buf.lines.splice(buf.row + 1, 0, rest)
buf.row++
buf.col = 0
buf.dirty = true
}
/**
* @param {{ lines: string[], row: number, col: number, dirty: boolean, undo: unknown[], redo: unknown[] }} buf
*/
function bareEditBackspace(buf) {
if (buf.col > 0) {
bareEditPushUndo(buf)
const line = buf.lines[buf.row]
buf.lines[buf.row] = line.slice(0, buf.col - 1) + line.slice(buf.col)
buf.col--
buf.dirty = true
return
}
if (buf.row > 0) {
bareEditPushUndo(buf)
const prevLen = buf.lines[buf.row - 1].length
buf.lines[buf.row - 1] += buf.lines[buf.row]
buf.lines.splice(buf.row, 1)
buf.row--
buf.col = prevLen
buf.dirty = true
}
}
/**
* @param {{ lines: string[], row: number, col: number, dirty: boolean, undo: unknown[], redo: unknown[] }} buf
*/
function bareEditDelete(buf) {
const line = buf.lines[buf.row]
if (buf.col < line.length) {
bareEditPushUndo(buf)
buf.lines[buf.row] = line.slice(0, buf.col) + line.slice(buf.col + 1)
buf.dirty = true
return
}
if (buf.row < buf.lines.length - 1) {
bareEditPushUndo(buf)
buf.lines[buf.row] += buf.lines[buf.row + 1]
buf.lines.splice(buf.row + 1, 1)
buf.dirty = true
}
}
/**
* @param {{ lines: string[], row: number, col: number, dirty: boolean, undo: unknown[], redo: unknown[] }} buf
* @param {'home'|'end'|'up'|'down'|'left'|'right'} key
*/
function bareEditMoveKey(buf, key) {
if (key === 'home') {
buf.col = 0
return
}
if (key === 'end') {
buf.col = buf.lines[buf.row].length
return
}
if (key === 'up') {
if (buf.row > 0) {
buf.row--
buf.col = Math.min(buf.col, buf.lines[buf.row].length)
}
return
}
if (key === 'down') {
if (buf.row < buf.lines.length - 1) {
buf.row++
buf.col = Math.min(buf.col, buf.lines[buf.row].length)
}
return
}
if (key === 'left') {
if (buf.col > 0) buf.col--
else if (buf.row > 0) {
buf.row--
buf.col = buf.lines[buf.row].length
}
return
}
if (key === 'right') {
const line = buf.lines[buf.row]
if (buf.col < line.length) buf.col++
else if (buf.row < buf.lines.length - 1) {
buf.row++
buf.col = 0
}
}
}
/**
* @param {{ lines: string[], row: number, col: number, dirty: boolean, undo: unknown[], redo: unknown[] }} buf
*/
function bareEditJoinAll(buf) {
return buf.lines.join('\n')
}
/**
* @param {{ lines: string[], row: number, col: number, dirty: boolean, undo: unknown[], redo: unknown[] }} buf
* @param {string} needle
* @param {number} fromRow
* @param {number} fromCol
* @returns {{ row: number, col: number } | null}
*/
function bareEditFindNext(buf, needle, fromRow, fromCol) {
if (!needle) return null
for (let r = fromRow; r < buf.lines.length; r++) {
const line = buf.lines[r] || ''
const start = r === fromRow ? fromCol : 0
const idx = line.indexOf(needle, start)
if (idx >= 0) return { row: r, col: idx }
}
for (let r = 0; r < fromRow; r++) {
const idx = (buf.lines[r] || '').indexOf(needle)
if (idx >= 0) return { row: r, col: idx }
}
const line = buf.lines[fromRow] || ''
const idx = line.indexOf(needle, 0)
if (idx >= 0 && idx < fromCol) return { row: fromRow, col: idx }
return null
}
/**
* @param {{ lines: string[], row: number, col: number }} buf
* @param {number} targetRow 1-based
*/
function bareEditGotoLine(buf, targetRow) {
const r = Math.max(1, Math.min(buf.lines.length, targetRow)) - 1
buf.row = r
buf.col = Math.min(buf.col, buf.lines[r].length)
}
@@ -0,0 +1,286 @@
/** Syntax highlighting for /bin/edit (line-oriented, best-effort). */
const EDIT_KW_JS =
/^(?:const|let|var|function|return|async|await|if|else|for|while|do|switch|case|break|continue|default|try|catch|finally|throw|new|typeof|instanceof|in|of|class|extends|super|this|static|import|export|from|as|default|void|delete|yield|enum|interface|type|public|private|protected|readonly)$/
/**
* @param {string} path
*/
function bareEditDetectLang(path) {
const p = String(path || '').toLowerCase()
const dot = p.lastIndexOf('.')
const ext = dot >= 0 ? p.slice(dot) : ''
if (ext === '.json') return 'json'
if (ext === '.md' || ext === '.markdown') return 'md'
if (ext === '.sh' || ext === '.bash' || ext === '.zsh') return 'shell'
if (
ext === '.js' ||
ext === '.mjs' ||
ext === '.cjs' ||
ext === '.ts' ||
ext === '.tsx' ||
ext === '.jsx'
)
return 'js'
return 'plain'
}
/**
* Merge overlapping spans (later wins) not used if we build non-overlapping.
* @param {Array<{ start: number, end: number, cls: string }>} spans
*/
function bareEditMergeSpans(spans) {
const s = spans.filter((x) => x.end > x.start).sort((a, b) => a.start - b.start || b.end - a.end)
/** @type {typeof spans} */
const out = []
for (const cur of s) {
const last = out[out.length - 1]
if (!last || cur.start >= last.end) {
out.push({ ...cur })
} else if (cur.end > last.end) {
if (cur.start > last.start) {
out[out.length - 1] = { start: last.start, end: cur.start, cls: last.cls }
out.push({ ...cur })
} else {
out[out.length - 1] = { ...cur }
}
}
}
return out
}
/**
* @param {string} line
* @returns {Array<{ start: number, end: number, cls: string }>}
*/
function bareEditSpansStringsCommentsJs(line) {
/** @type {Array<{ start: number, end: number, cls: string }>} */
const spans = []
let i = 0
while (i < line.length) {
const c = line[i]
const next = line[i + 1]
if (c === '/' && next === '/') {
spans.push({ start: i, end: line.length, cls: 'comment' })
break
}
if (c === '/' && next === '*') {
let j = i + 2
while (j < line.length - 1) {
if (line[j] === '*' && line[j + 1] === '/') {
j += 2
break
}
j++
}
if (j > line.length) j = line.length
spans.push({ start: i, end: j, cls: 'comment' })
i = j
continue
}
if (c === '"' || c === "'" || c === '`') {
const q = c
const start = i
i++
while (i < line.length) {
if (line[i] === '\\') {
i += 2
continue
}
if (line[i] === q) {
i++
break
}
i++
}
spans.push({ start, end: i, cls: 'string' })
continue
}
i++
}
return bareEditMergeSpans(spans)
}
/**
* @param {string} segment code only (no strings/comments inside)
*/
function bareEditSpansKeywordsNumbers(segment, offset) {
/** @type {Array<{ start: number, end: number, cls: string }>} */
const out = []
const re = /\b([A-Za-z_$][\w$]*)\b|\b(\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)\b/g
let m
while ((m = re.exec(segment)) !== null) {
if (m[1]) {
if (EDIT_KW_JS.test(m[1])) {
out.push({
start: offset + m.index,
end: offset + m.index + m[1].length,
cls: 'keyword'
})
}
} else if (m[2]) {
out.push({
start: offset + m.index,
end: offset + m.index + m[2].length,
cls: 'number'
})
}
}
return out
}
/**
* @param {string} line
* @param {number} gapStart
* @param {number} gapEnd
* @param {Array<{ start: number, end: number, cls: string }>} base
*/
function bareEditFillGapKeywords(line, gapStart, gapEnd, base) {
if (gapEnd <= gapStart) return
const seg = line.slice(gapStart, gapEnd)
const extra = bareEditSpansKeywordsNumbers(seg, gapStart)
for (const e of extra) base.push(e)
}
/**
* @param {string} line
*/
function bareEditHighlightJsLine(line) {
const sc = bareEditSpansStringsCommentsJs(line)
if (sc.length === 0) {
const all = /** @type {typeof sc} */ ([])
bareEditFillGapKeywords(line, 0, line.length, all)
return all.sort((a, b) => a.start - b.start)
}
/** @type {typeof sc} */
const out = [...sc]
let cursor = 0
for (const sp of sc) {
bareEditFillGapKeywords(line, cursor, sp.start, out)
cursor = sp.end
}
bareEditFillGapKeywords(line, cursor, line.length, out)
return out.sort((a, b) => a.start - b.start)
}
/**
* @param {string} line
*/
function bareEditHighlightJsonLine(line) {
const t = line.trimStart()
if (t.startsWith('//')) {
return [{ start: line.indexOf('//'), end: line.length, cls: 'comment' }]
}
/** @type {Array<{ start: number, end: number, cls: string }>} */
const spans = []
const re = /("(?:\\.|[^"\\])*")\s*:|("(?:\\.|[^"\\])*")|(\btrue|false|null\b)|(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g
let m
while ((m = re.exec(line)) !== null) {
if (m[1]) {
const keyEnd = m.index + m[1].length
spans.push({ start: m.index, end: keyEnd, cls: 'keyword' })
} else if (m[2]) {
spans.push({
start: m.index,
end: m.index + m[2].length,
cls: 'string'
})
} else if (m[3]) {
spans.push({
start: m.index,
end: m.index + m[3].length,
cls: 'keyword'
})
} else if (m[4]) {
spans.push({
start: m.index,
end: m.index + m[4].length,
cls: 'number'
})
}
}
return spans.sort((a, b) => a.start - b.start)
}
/**
* @param {string} line
*/
function bareEditHighlightShellLine(line) {
const idx = line.indexOf('#')
if (idx >= 0) {
return [{ start: idx, end: line.length, cls: 'comment' }]
}
/** @type {Array<{ start: number, end: number, cls: string }>} */
const spans = []
const kw =
/^\s*(if|then|else|elif|fi|for|while|do|done|case|esac|function|return|export|local|readonly|source|\.)[\s#;]|\b(if|then|else|elif|fi|for|in|do|done|case|esac|function|return|export|local|readonly)\b/g
let m
while ((m = kw.exec(line)) !== null) {
const word = m[1] || m[2]
if (!word) continue
const start = m.index + m[0].indexOf(word)
spans.push({ start, end: start + word.length, cls: 'keyword' })
}
const dq = /"(?:\\.|[^"\\])*"/g
while ((m = dq.exec(line)) !== null) {
spans.push({ start: m.index, end: m.index + m[0].length, cls: 'string' })
}
const sq = /'[^']*'/g
while ((m = sq.exec(line)) !== null) {
spans.push({ start: m.index, end: m.index + m[0].length, cls: 'string' })
}
return spans.sort((a, b) => a.start - b.start)
}
/**
* @param {string} line
*/
function bareEditHighlightMdLine(line) {
/** @type {Array<{ start: number, end: number, cls: string }>} */
const spans = []
if (/^\s*#{1,6}\s/.test(line)) {
const m = line.match(/^\s*(#{1,6}\s.*)$/)
if (m) {
const i = line.indexOf(m[1])
spans.push({ start: i, end: line.length, cls: 'keyword' })
}
return spans
}
if (/^\s*(?:[-*+]|\d+\.)\s/.test(line)) {
const m = line.match(/^\s*((?:[-*+]|\d+\.)\s.*)$/)
if (m) {
const i = line.indexOf(m[1])
spans.push({ start: i, end: line.length, cls: 'comment' })
}
return spans
}
const bold = /\*\*[^*]+\*\*|__[^_]+__/g
let m
while ((m = bold.exec(line)) !== null) {
spans.push({ start: m.index, end: m.index + m[0].length, cls: 'string' })
}
const code = /`[^`]+`/g
while ((m = code.exec(line)) !== null) {
spans.push({ start: m.index, end: m.index + m[0].length, cls: 'number' })
}
return spans.sort((a, b) => a.start - b.start)
}
/**
* @param {string} line
* @param {string} lang
*/
function bareEditHighlightLine(line, lang) {
switch (lang) {
case 'js':
return bareEditHighlightJsLine(line)
case 'json':
return bareEditHighlightJsonLine(line)
case 'shell':
return bareEditHighlightShellLine(line)
case 'md':
return bareEditHighlightMdLine(line)
default:
return []
}
}
@@ -0,0 +1,121 @@
/** TTY key parsing for /bin/edit: consume one logical key from a mutable byte queue. */
/**
* @param {number} b1
*/
function bareEditUtf8TrailCount(b1) {
if (b1 >= 0xc0 && b1 < 0xe0) return 1
if (b1 >= 0xe0 && b1 < 0xf0) return 2
if (b1 >= 0xf0) return 3
return 0
}
/**
* @param {number[]} bytes
*/
function bareEditUtf8DecodeKey(bytes) {
try {
const u = new Uint8Array(bytes)
if (typeof TextDecoder !== 'undefined') {
return new TextDecoder('utf-8', { fatal: false }).decode(u)
}
} catch {
/* fall through */
}
let s = ''
for (const b of bytes) s += String.fromCharCode(b)
return s
}
/**
* @param {string} seq CSI payload after ESC [, including final byte (e.g. "A", "1;5A", "3~")
*/
function bareEditCsiToEvent(seq) {
if (seq === '3~') return { type: 'ctrl', code: 'delete' }
if (seq === '5~') return { type: 'nav', key: 'pageup' }
if (seq === '6~') return { type: 'nav', key: 'pagedown' }
const last = seq.charAt(seq.length - 1)
if (last === '~') {
if (seq === '1~' || seq === '7~') return { type: 'nav', key: 'home' }
if (seq === '4~' || seq === '8~') return { type: 'nav', key: 'end' }
return { type: 'unknown' }
}
if (last === 'A' || last === 'B' || last === 'C' || last === 'D') {
const map = { A: 'up', B: 'down', C: 'right', D: 'left' }
return { type: 'nav', key: map[last] }
}
if (last === 'H') return { type: 'nav', key: 'home' }
if (last === 'F') return { type: 'nav', key: 'end' }
return { type: 'unknown' }
}
/**
* @param {number} b3 byte after ESC O
*/
function bareEditSs3ToEvent(b3) {
if (b3 === 72) return { type: 'nav', key: 'home' }
if (b3 === 70) return { type: 'nav', key: 'end' }
if (b3 === 65) return { type: 'nav', key: 'up' }
if (b3 === 66) return { type: 'nav', key: 'down' }
if (b3 === 67) return { type: 'nav', key: 'right' }
if (b3 === 68) return { type: 'nav', key: 'left' }
return { type: 'unknown' }
}
/**
* @param {number[]} q mutable queue (front = index 0)
* @returns {Record<string, unknown> | null} null if more bytes needed
*/
function bareEditTryConsumeKey(q) {
if (!q.length) return null
const b1 = q[0]
if (b1 === 3) {
q.shift()
return { type: 'ctrl', code: 'interrupt' }
}
if (b1 === 8 || b1 === 127) {
q.shift()
return { type: 'ctrl', code: 'backspace' }
}
if (b1 === 13 || b1 === 10) {
q.shift()
return { type: 'key', ch: '\n' }
}
if (b1 === 9) {
q.shift()
return { type: 'key', ch: '\t' }
}
if (b1 === 27) {
if (q.length < 2) return null
const b2 = q[1]
if (b2 === 91) {
let i = 2
while (i < q.length) {
const b = q[i]
if (b >= 0x40 && b <= 0x7e) {
const seq = String.fromCharCode.apply(null, q.slice(2, i + 1))
q.splice(0, i + 1)
return bareEditCsiToEvent(seq)
}
i++
}
return null
}
if (b2 === 79) {
if (q.length < 3) return null
const b3 = q[2]
q.splice(0, 3)
return bareEditSs3ToEvent(b3)
}
q.splice(0, 2)
return { type: 'key', ch: String.fromCharCode(b2) }
}
if (b1 < 0x20) {
q.shift()
return { type: 'ctrl', code: b1 }
}
const need = bareEditUtf8TrailCount(b1)
if (q.length < 1 + need) return null
const chunk = q.splice(0, 1 + need)
return { type: 'key', ch: bareEditUtf8DecodeKey(chunk) }
}
+466
View File
@@ -0,0 +1,466 @@
/** Full-screen TUI for /bin/edit (raw TTY, fish suspend/resume). */
/**
* @param {unknown} chunk
* @returns {number[]}
*/
function bareEditChunkBytes(chunk) {
if (chunk == null) return []
if (typeof chunk === 'string') {
const out = []
for (let i = 0; i < chunk.length; i++) out.push(chunk.charCodeAt(i) & 0xff)
return out
}
const len = /** @type {{ length: number, [k: number]: number }} */ (chunk).length
const out = []
for (let i = 0; i < len; i++) out.push(Number(chunk[i]) & 0xff)
return out
}
/**
* @param {{ nextByte: () => Promise<number|undefined>, dispose?: () => void, _keyq?: number[] }} reader
*/
async function bareEditReadKey(reader) {
reader._keyq = reader._keyq || []
const q = reader._keyq
for (;;) {
const ev = bareEditTryConsumeKey(q)
if (ev) return ev
const b = await reader.nextByte()
if (b === undefined) {
if (!q.length) return { type: 'eof' }
if (q.length === 1 && q[0] === 27) {
q.length = 0
return { type: 'key', ch: '\x1b' }
}
const lone = q.shift()
if (lone !== undefined && lone < 0x20) {
return { type: 'ctrl', code: lone }
}
if (lone !== undefined) {
return { type: 'key', ch: String.fromCharCode(lone) }
}
return { type: 'eof' }
}
q.push(b)
}
}
/**
* @param {import('stream').Readable} stdin
*/
function bareEditCreateStdinReader(stdin) {
/** @type {number[]} */
const bytes = []
/** @type {(() => void)[]} */
const waiters = []
function drain() {
while (waiters.length && bytes.length) {
const w = waiters.shift()
if (w) w()
}
}
/** @param {unknown} chunk */
function onData(chunk) {
bytes.push(...bareEditChunkBytes(chunk))
drain()
}
stdin.on('data', onData)
return {
nextByte() {
if (bytes.length) return Promise.resolve(bytes.shift())
return new Promise((resolve) => {
waiters.push(() => resolve(bytes.shift()))
})
},
dispose() {
stdin.removeListener('data', onData)
}
}
}
/**
* @param {Record<string, unknown>} ctx
* @param {import('stream').Writable} stdout
* @param {string} s
*/
function bareEditWrite(ctx, stdout, s) {
if (!stdout || typeof stdout.write !== 'function') return
try {
stdout.write(s)
} catch {
try {
ctx.console?.error?.('edit: stdout write failed')
} catch {
/* ignore */
}
}
}
/**
* @param {Record<string, unknown>} ctx
* @param {{ path: string, initialText: string, argv0?: string }} opts
*/
async function bareOsRunEditTui(ctx, opts) {
const vfs = ctx.vfs
const b4a = ctx.b4a
const path = opts.path || 'Untitled'
const display = opts.argv0 || 'edit'
const lang = bareEditDetectLang(path)
const buf = bareEditCreateBuffer(opts.initialText)
const useColor = bareEditUseColor(ctx)
const stdin = /** @type {import('stream').Readable | undefined} */ (
ctx.replStdin
)
const stdout = bareEditResolveStdout(ctx)
if (!stdin || !stdout) {
ctx.console.error('edit: missing stdin/stdout')
ctx.exitCode = 1
return
}
let scrollRow = 0
let scrollCol = 0
let savePath = path
/** @type {'edit'|'quit_confirm'|'help'|'prompt_search'|'prompt_goto'|'prompt_saveas'} */
let mode = 'edit'
let promptBuf = ''
let promptTitle = ''
let lastSearch = ''
function termDims() {
const env =
ctx.env && typeof ctx.env === 'object'
? /** @type {Record<string, string>} */ (ctx.env)
: {}
const cols = /** @type {{ columns?: number }} */ (stdout).columns ||
parseInt(env.COLUMNS || '80', 10) ||
80
const rows = /** @type {{ rows?: number }} */ (stdout).rows ||
parseInt(env.LINES || '24', 10) ||
24
return { cols: Math.max(40, cols), rows: Math.max(8, rows) }
}
function ensureScroll() {
const { cols, rows } = termDims()
const contentH = rows - 2
if (buf.row < scrollRow) scrollRow = buf.row
if (buf.row >= scrollRow + contentH) scrollRow = buf.row - contentH + 1
if (scrollRow < 0) scrollRow = 0
if (scrollCol > buf.col) scrollCol = buf.col
if (buf.col - scrollCol >= cols) scrollCol = buf.col - cols + 1
if (scrollCol < 0) scrollCol = 0
}
function draw() {
const { cols, rows } = termDims()
const contentH = Math.max(1, rows - 2)
ensureScroll()
let out = ''
out += '\x1b[?25l\x1b[2J\x1b[H'
if (mode === 'help') {
out +=
'Bare OS edit — help\r\n\r\n' +
'^O ^S Save ^X Exit\r\n' +
'^W Search ^_ Go to line\r\n' +
'^Z Undo ^Y Redo\r\n' +
'Arrows move cursor; Home/End; Backspace/Del\r\n\r\n' +
'Press any key to return.\r\n'
out += bareEditCup(8, 1) + '\x1b[?25h'
bareEditWrite(ctx, stdout, out)
return
}
if (mode === 'quit_confirm') {
for (let row = 1; row <= contentH; row++) {
out += bareEditCup(row, 1) + '\x1b[K'
}
const msg = 'Save modified buffer? Y Yes N No ^C Cancel'
const st = bareEditSgr('status', useColor) + msg + EDIT_ANSI_RESET
out += bareEditCup(contentH + 1, 1) + '\x1b[K' + st
out += bareEditCup(contentH + 1, 1) + '\x1b[?25h'
bareEditWrite(ctx, stdout, out)
return
}
if (mode === 'prompt_search' || mode === 'prompt_goto' || mode === 'prompt_saveas') {
const editorLines = Math.max(0, contentH - 1)
for (let r = 0; r < editorLines; r++) {
const lineIndex = scrollRow + r
const line = buf.lines[lineIndex] || ''
const spans = bareEditHighlightLine(line, lang)
const vis = bareEditPaintLineWindow(line, spans, scrollCol, cols, useColor)
out += bareEditCup(1 + r, 1) + '\x1b[K' + vis
}
const barBody =
bareEditSgr('status', useColor) +
promptTitle +
promptBuf +
(useColor ? '\x1b[0m' : '')
const promptRow = editorLines + 1
out += bareEditCup(promptRow, 1) + '\x1b[K' + barBody
const promptCol = 1 + promptTitle.length + promptBuf.length
out += bareEditCup(promptRow, promptCol) + '\x1b[?25h'
bareEditWrite(ctx, stdout, out)
return
}
for (let r = 0; r < contentH; r++) {
const lineIndex = scrollRow + r
const line = buf.lines[lineIndex] || ''
const spans = bareEditHighlightLine(line, lang)
const vis = bareEditPaintLineWindow(line, spans, scrollCol, cols, useColor)
out += bareEditCup(1 + r, 1) + '\x1b[K' + vis
}
const dirtyMark = buf.dirty ? ' [Modified]' : ''
const statusLine =
(useColor ? bareEditSgr('status', true) : '') +
display +
' ' +
savePath +
dirtyMark +
' ' +
(buf.row + 1) +
',' +
(buf.col + 1) +
(useColor ? EDIT_ANSI_RESET : '')
const helpLine =
(useColor ? bareEditSgr('dim', true) : '') +
'^G Help ^O/^S Save ^X Exit ^W Find ^_ Line ^Z Undo ^Y Redo' +
(useColor ? EDIT_ANSI_RESET : '')
out += bareEditCup(contentH + 1, 1) + '\x1b[K' + statusLine
out += bareEditCup(contentH + 2, 1) + '\x1b[K' + helpLine
const curRow = 1 + (buf.row - scrollRow)
const curCol = 1 + (buf.col - scrollCol)
const tr = Math.min(Math.max(1, curRow), contentH)
const tc = Math.min(Math.max(1, curCol), cols)
out += bareEditCup(tr, tc) + '\x1b[?25h'
bareEditWrite(ctx, stdout, out)
}
async function doSave(targetPath) {
const text = bareEditJoinAll(buf)
try {
await vfs.writeFile(targetPath, b4a.from(text))
buf.dirty = false
savePath = targetPath
return true
} catch (e) {
ctx.console.error('edit: ' + ((e && e.message) || String(e)))
return false
}
}
const reader = bareEditCreateStdinReader(stdin)
let suspended = false
try {
if (typeof ctx.suspendReplForSubprocess === 'function') {
ctx.suspendReplForSubprocess()
suspended = true
}
if (typeof stdin.setRawMode === 'function') stdin.setRawMode(true)
if (typeof stdin.resume === 'function') stdin.resume()
draw()
for (;;) {
const ev = await bareEditReadKey(reader)
if (ev.type === 'eof') break
if (mode === 'help') {
mode = 'edit'
draw()
continue
}
if (mode === 'quit_confirm') {
if (ev.type === 'ctrl' && ev.code === 'interrupt') {
mode = 'edit'
draw()
continue
}
if (ev.type === 'key' && ev.ch) {
const u = ev.ch.toUpperCase()
if (u === 'Y') {
await doSave(savePath)
break
}
if (u === 'N') break
}
continue
}
if (
mode === 'prompt_search' ||
mode === 'prompt_goto' ||
mode === 'prompt_saveas'
) {
if (ev.type === 'ctrl' && ev.code === 'interrupt') {
mode = 'edit'
draw()
continue
}
if (ev.type === 'key' && ev.ch === '\n') {
if (mode === 'prompt_search') {
lastSearch = promptBuf
if (lastSearch) {
const hit = bareEditFindNext(
buf,
lastSearch,
buf.row,
buf.col + 1
)
if (!hit) {
const hit2 = bareEditFindNext(buf, lastSearch, 0, 0)
if (hit2) {
buf.row = hit2.row
buf.col = hit2.col
}
} else {
buf.row = hit.row
buf.col = hit.col
}
}
} else if (mode === 'prompt_goto') {
const n = parseInt(promptBuf.trim(), 10)
if (Number.isFinite(n)) bareEditGotoLine(buf, n)
} else if (mode === 'prompt_saveas') {
const p = promptBuf.trim()
if (p) {
savePath = p
await doSave(savePath)
}
}
mode = 'edit'
draw()
continue
}
if (ev.type === 'ctrl' && ev.code === 'backspace') {
promptBuf = promptBuf.slice(0, -1)
draw()
continue
}
if (ev.type === 'key' && ev.ch && ev.ch !== '\n' && ev.ch !== '\t') {
if (ev.ch >= ' ') promptBuf += ev.ch
draw()
continue
}
continue
}
if (ev.type === 'nav') {
if (ev.key === 'pageup') {
const { rows } = termDims()
const step = Math.max(1, rows - 4)
buf.row = Math.max(0, buf.row - step)
bareEditClampCursor(buf)
} else if (ev.key === 'pagedown') {
const { rows } = termDims()
const step = Math.max(1, rows - 4)
buf.row = Math.min(buf.lines.length - 1, buf.row + step)
bareEditClampCursor(buf)
} else bareEditMoveKey(buf, ev.key)
draw()
continue
}
if (ev.type === 'ctrl') {
if (ev.code === 'backspace') {
bareEditBackspace(buf)
draw()
continue
}
if (ev.code === 'delete') {
bareEditDelete(buf)
draw()
continue
}
if (ev.code === 'interrupt') {
/* ignore in edit */
continue
}
const code = typeof ev.code === 'number' ? ev.code : 0
if (code === 27) {
/* lone ESC fragment; do not insert */
continue
}
if (code === 24) {
if (buf.dirty) {
mode = 'quit_confirm'
draw()
} else break
continue
}
if (code === 19) {
await doSave(savePath)
draw()
continue
}
if (code === 15) {
mode = 'prompt_saveas'
promptTitle = 'File: '
promptBuf = savePath
draw()
continue
}
if (code === 23) {
mode = 'prompt_search'
promptTitle = 'Search: '
promptBuf = lastSearch
draw()
continue
}
if (code === 7) {
mode = 'help'
draw()
continue
}
if (code === 31) {
mode = 'prompt_goto'
promptTitle = 'Go to line: '
promptBuf = ''
draw()
continue
}
if (code === 26) {
bareEditUndo(buf)
draw()
continue
}
if (code === 25) {
bareEditRedo(buf)
draw()
continue
}
continue
}
if (ev.type === 'key' && ev.ch) {
if (ev.ch === '\n') bareEditNewline(buf)
else if (ev.ch === '\t') bareEditInsertChar(buf, ' ')
else bareEditInsertChar(buf, ev.ch)
draw()
}
}
} finally {
try {
bareEditWrite(ctx, stdout, '\x1b[?25h\x1b[0m')
} catch {
/* ignore */
}
reader.dispose()
try {
if (typeof stdin.setRawMode === 'function') stdin.setRawMode(false)
} catch {
/* ignore */
}
if (suspended && typeof ctx.resumeReplAfterSubprocess === 'function') {
ctx.resumeReplAfterSubprocess()
}
}
}
@@ -0,0 +1,19 @@
{
"name": "edit",
"section": 1,
"title": "terminal file editor with syntax highlighting",
"synopsis": ["edit [file]", "edit -h|--help"],
"description": "Full-screen TUI editor (nano-style) for the Bare OS shell. Uses the session TTY (ctx.replStdin / replStdout), suspends the fish readline layer while active, and reads/writes paths via the VFS. Syntax highlighting is best-effort for JavaScript/TypeScript, JSON, shell, Markdown, and plain text. Files larger than 2,000,000 characters are rejected. The shell alias nano invokes edit.",
"options": [
{
"flag": "-h, --help",
"meaning": "Print usage and exit."
}
],
"keywords": ["edit", "nano", "editor", "tty", "syntax"],
"bareOsNotes": "Requires an interactive TTY (stdin.isTTY). Uses ctx.suspendReplForSubprocess / resumeReplAfterSubprocess around raw mode.",
"examples": [
{ "caption": "edit a file", "code": "edit README.md" },
{ "caption": "same via alias", "code": "nano foo.js" }
]
}
@@ -0,0 +1,11 @@
{
"name": "nano",
"section": 1,
"title": "alias for edit — terminal file editor",
"synopsis": ["nano [file]"],
"description": "The /bin/nano script is identical to edit. In the default shell, the name nano is also an alias for edit. See edit(1) for behavior, key bindings, and requirements.",
"options": [],
"keywords": ["nano", "edit", "editor"],
"bareOsNotes": "Same implementation as edit; see man edit.",
"examples": [{ "caption": "open a file", "code": "nano ~/.barerc" }]
}
+2 -1
View File
@@ -5,6 +5,7 @@
"type": "module", "type": "module",
"description": "Build JS /bin utilities for bare-operating-system (concat + stage to kernel/)", "description": "Build JS /bin utilities for bare-operating-system (concat + stage to kernel/)",
"scripts": { "scripts": {
"build": "node ./build.mjs" "build": "node ./build.mjs",
"test": "node ./test/edit-key-parse.test.mjs"
} }
} }
+50
View File
@@ -0,0 +1,50 @@
async function run(ctx, argv) {
const args = argv.slice(1)
if (args.includes('-h') || args.includes('--help')) {
ctx.console.log(
'usage: ' +
(argv[0] || 'edit') +
' [file]\n' +
'Terminal editor with syntax highlighting. Requires a TTY.\n' +
'See man edit for key bindings.'
)
return
}
let path = 'Untitled'
for (const a of args) {
if (a && !String(a).startsWith('-')) {
path = String(a)
break
}
}
const stdin = ctx.replStdin
const stdout = ctx.replStdout || ctx.stdout
if (!stdin || !/** @type {{ isTTY?: boolean }} */ (stdin).isTTY) {
ctx.console.error('edit: a terminal (TTY) is required')
ctx.exitCode = 1
return
}
if (!stdout) {
ctx.console.error('edit: missing stdout')
ctx.exitCode = 1
return
}
let initial = ''
try {
const fileBuf = await ctx.vfs.readFile(path)
if (fileBuf) initial = ctx.b4a.toString(fileBuf)
} catch {
initial = ''
}
const maxChars = 2000000
if (initial.length > maxChars) {
ctx.console.error('edit: file too large (max ' + maxChars + ' characters)')
ctx.exitCode = 1
return
}
await bareOsRunEditTui(ctx, {
path,
initialText: initial,
argv0: argv[0] || 'edit'
})
}
+1 -1
View File
@@ -1,6 +1,6 @@
async function run(ctx, argv) { async function run(ctx, argv) {
ctx.console.log( ctx.console.log(
'Bare OS — default user: guest | shell builtins: alias, barerc, cd, command, export, exit, login, logout, readonly, type, umask, unalias, unset, : | /bin: awk basename cat chgrp chmod chown cksum clear cp crontab curl cut date dirname dircolors du echo env exit false find getconf grep head hdms help hostname id journalctl jq ln login logout logname ls man mkdir mkfifo mv nl od pathchk printenv printf pwd readlink rm rmdir savevault sed seq sleep sort stat systemctl tail tee test theme time touch tr true tty uname wc wget which whoami xargs' 'Bare OS — default user: guest | shell builtins: alias, barerc, cd, command, export, exit, login, logout, readonly, type, umask, unalias, unset, : | /bin: awk basename cat chgrp chmod chown cksum clear cp crontab curl cut date dirname dircolors du edit echo env exit false find getconf grep head hdms help hostname id journalctl jq ln login logout logname ls man mkdir mkfifo mktemp nano mv nl od pathchk printenv printf pwd readlink rm rmdir savevault sed seq sleep sort stat systemctl tail tee test theme time touch tr true tty uname wc wget which whoami xargs'
) )
ctx.console.log( ctx.console.log(
'Docs: man <command> | man handbook (full handbook, section 7) | man bare-os-shell | man -k <word> | man -l' 'Docs: man <command> | man handbook (full handbook, section 7) | man bare-os-shell | man -k <word> | man -l'
@@ -0,0 +1,65 @@
/**
* Loads lib/edit-key-parse.js in an isolated function scope (same source as /bin/edit preamble).
*/
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = dirname(fileURLToPath(import.meta.url))
const parseSrc = readFileSync(join(__dirname, '../lib/edit-key-parse.js'), 'utf8')
const hook = {}
new Function(
'exports',
`${parseSrc}\nexports.bareEditTryConsumeKey = bareEditTryConsumeKey;`
)(hook)
const { bareEditTryConsumeKey } = hook
function consumeAll(bytes) {
const q = [...bytes]
const out = []
while (q.length) {
const ev = bareEditTryConsumeKey(q)
assert.ok(ev, 'expected complete key, queue=' + JSON.stringify(q))
out.push(ev)
}
return out
}
assert.equal(bareEditTryConsumeKey([97])?.type, 'key')
assert.equal(bareEditTryConsumeKey([97])?.ch, 'a')
const upCsi = consumeAll([27, 91, 65])
assert.deepEqual(upCsi, [{ type: 'nav', key: 'up' }])
const upCsiNum = consumeAll([27, 91, 49, 59, 53, 65])
assert.deepEqual(upCsiNum, [{ type: 'nav', key: 'up' }])
const leftCsi = consumeAll([27, 91, 68])
assert.deepEqual(leftCsi, [{ type: 'nav', key: 'left' }])
const ss3Up = consumeAll([27, 79, 65])
assert.deepEqual(ss3Up, [{ type: 'nav', key: 'up' }])
const ss3Left = consumeAll([27, 79, 68])
assert.deepEqual(ss3Left, [{ type: 'nav', key: 'left' }])
const del = consumeAll([27, 91, 51, 126])
assert.deepEqual(del, [{ type: 'ctrl', code: 'delete' }])
/* ESC must not be consumed as generic ctrl before CSI: order test */
const q = [27, 91, 66]
const ev1 = bareEditTryConsumeKey(q)
assert.deepEqual(ev1, { type: 'nav', key: 'down' })
assert.equal(q.length, 0)
/* Incomplete CSI waits for more bytes */
const q2 = [27, 91]
assert.equal(bareEditTryConsumeKey(q2), null)
assert.deepEqual(q2, [27, 91])
q2.push(67)
const ev2 = bareEditTryConsumeKey(q2)
assert.deepEqual(ev2, { type: 'nav', key: 'right' })
console.log('edit-key-parse tests ok')
+1 -1
View File
@@ -16,7 +16,7 @@ Files in this directory are **read from disk by the seeder** (or copied into `pa
## Contents ## Contents
- **`init.js`** — Kernel entry: must define `async function start(ctx)`. Boot order: **`/etc/os-release`** → **`/etc/motd`** → optional **`/etc/bare-os/rc.profile.<profile>`** (profile from **`BARE_OS_BOOT_PROFILE`** or first line of **`/etc/bare-os/profile`**; the booter mirrors the resolved name in **`ctx.env.BARE_OS_BOOT_PROFILE_RESOLVED`** and **`/run/bare-os/boot_profile`**) → **`/etc/bare-os/rc`** → **`/etc/bare-os/rc.d/*`** (sorted; digit-prefixed names only; skip dotfiles, `*~`, `README*`, `*.md`; optional **`BARE_OS_RC_D_SKIP`** comma list and **`prefix*`** patterns) → optional **`/etc/bare-os/rc.local`** → **`/etc/bare-os/kernel.d/*`** (same rules as **`rc.d`**) → banner → when **`BARE_OS_SKIP_REPL`**, optional **onboot** lines from **`BARE_OS_ONBOOT`** or **`/etc/bare-os/onboot`** → **`readLine` / `execLine`** loop. Boot **`execLine`** errors in trusted snippets are logged; with **`BARE_OS_BOOT_STRICT=1`** or **`true`**, the first throw calls **`requestBooterExit(1)`** and stops later boot phases. Custom kernels may call **`ctx.registerKernelShutdownHook(fn)`** before initd disposers; use **`ctx.bareOsRuntimeCaps`** for limits, pseudo paths, and **`features`** ([`developer-guide/02-the-context-object.md`](../developer-guide/02-the-context-object.md)). - **`init.js`** — Kernel entry: must define `async function start(ctx)`. Boot order: **`/etc/os-release`** → **`/etc/motd`** → optional **`/etc/bare-os/rc.profile.<profile>`** (profile from **`BARE_OS_BOOT_PROFILE`** or first line of **`/etc/bare-os/profile`**; the booter mirrors the resolved name in **`ctx.env.BARE_OS_BOOT_PROFILE_RESOLVED`** and **`/run/bare-os/boot_profile`**) → **`/etc/bare-os/rc`** → **`/etc/bare-os/rc.d/*`** (sorted; digit-prefixed names only; skip dotfiles, `*~`, `README*`, `*.md`; optional **`BARE_OS_RC_D_SKIP`** comma list and **`prefix*`** patterns) → optional **`/etc/bare-os/rc.local`** → **`/etc/bare-os/kernel.d/*`** (same rules as **`rc.d`**) → banner → when **`BARE_OS_SKIP_REPL`**, optional **onboot** lines from **`BARE_OS_ONBOOT`** or **`/etc/bare-os/onboot`** → **`readLine` / `execLine`** loop. Boot **`execLine`** errors in trusted snippets are logged; with **`BARE_OS_BOOT_STRICT=1`** or **`true`**, the first throw calls **`requestBooterExit(1)`** and stops later boot phases. Custom kernels may call **`ctx.registerKernelShutdownHook(fn)`** before initd disposers; use **`ctx.bareOsRuntimeCaps`** for limits, pseudo paths, and **`features`** ([`developer-guide/02-the-context-object.md`](../developer-guide/02-the-context-object.md)).
- **`bin/`** — **Tier-1 utilities** built by [bare-os-coreutils](../packages/bare-os-coreutils/README.md). Each file is **`runtime.js`** + optional **`lib/*-engine.js`** (**`sed`**, **`awk`**) or **`lib/man-render.js`** (**`man`**) + **`async function run(ctx, argv)`** (no ESM **`import`** in **`src/`**). - **`bin/`** — **Tier-1 utilities** built by [bare-os-coreutils](../packages/bare-os-coreutils/README.md). Each file is **`runtime.js`** + optional **`lib/*-engine.js`** (**`sed`**, **`awk`**), **`lib/man-render.js`** (**`man`**), **`lib/edit-*.js`** (**`edit`** / **`nano`** — same **`src/edit.js`**), lscolors chunks (**`ls`**, **`dircolors`**), etc. + **`async function run(ctx, argv)`** (no ESM **`import`** in **`src/`**). **`/bin/nano`** duplicates **`/bin/edit`** for familiarity; the shells default **`nano``edit`** alias uses the **`edit`** command name after expansion.
- **`lib/bare/`** — Optional IIFE bundles + **`manifest.json`** for **`ctx.bare`** drive merge, built by [bare-os-bare-libs](../packages/bare-os-bare-libs/README.md). Same trust model as **`bin/`** (trusted seeded image). - **`lib/bare/`** — Optional IIFE bundles + **`manifest.json`** for **`ctx.bare`** drive merge, built by [bare-os-bare-libs](../packages/bare-os-bare-libs/README.md). Same trust model as **`bin/`** (trusted seeded image).
- **`share/man/man.json`** — Merged manual database for **`/bin/man`** (built by **`bare-os-coreutils`**; see [handbook ch.10](../handbook/10-manpages-and-online-help.md)). - **`share/man/man.json`** — Merged manual database for **`/bin/man`** (built by **`bare-os-coreutils`**; see [handbook ch.10](../handbook/10-manpages-and-online-help.md)).
- **`etc/os-release`** — Static OS metadata (`NAME`, `VERSION`, …). - **`etc/os-release`** — Static OS metadata (`NAME`, `VERSION`, …).
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -62,7 +62,7 @@ function barePosixBlocks(size) {
async function run(ctx, argv) { async function run(ctx, argv) {
ctx.console.log( ctx.console.log(
'Bare OS — default user: guest | shell builtins: alias, barerc, cd, command, export, exit, login, logout, readonly, type, umask, unalias, unset, : | /bin: awk basename cat chgrp chmod chown cksum clear cp crontab curl cut date dirname dircolors du echo env exit false find getconf grep head hdms help hostname id journalctl jq ln login logout logname ls man mkdir mkfifo mv nl od pathchk printenv printf pwd readlink rm rmdir savevault sed seq sleep sort stat systemctl tail tee test theme time touch tr true tty uname wc wget which whoami xargs' 'Bare OS — default user: guest | shell builtins: alias, barerc, cd, command, export, exit, login, logout, readonly, type, umask, unalias, unset, : | /bin: awk basename cat chgrp chmod chown cksum clear cp crontab curl cut date dirname dircolors du edit echo env exit false find getconf grep head hdms help hostname id journalctl jq ln login logout logname ls man mkdir mkfifo mktemp nano mv nl od pathchk printenv printf pwd readlink rm rmdir savevault sed seq sleep sort stat systemctl tail tee test theme time touch tr true tty uname wc wget which whoami xargs'
) )
ctx.console.log( ctx.console.log(
'Docs: man <command> | man handbook (full handbook, section 7) | man bare-os-shell | man -k <word> | man -l' 'Docs: man <command> | man handbook (full handbook, section 7) | man bare-os-shell | man -k <word> | man -l'
File diff suppressed because it is too large Load Diff
@@ -7,18 +7,18 @@
"b4a" "b4a"
] ]
}, },
{
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
"keys": [
"hypercoreIdEncoding"
]
},
{ {
"path": "/lib/bare/bundles/safetyCatch.js", "path": "/lib/bare/bundles/safetyCatch.js",
"keys": [ "keys": [
"safetyCatch" "safetyCatch"
] ]
}, },
{
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
"keys": [
"hypercoreIdEncoding"
]
},
{ {
"path": "/lib/bare/bundles/compactEncoding.js", "path": "/lib/bare/bundles/compactEncoding.js",
"keys": [ "keys": [
@@ -37,18 +37,18 @@
"protomux" "protomux"
] ]
}, },
{
"path": "/lib/bare/bundles/bareEvents.js",
"keys": [
"bareEvents"
]
},
{ {
"path": "/lib/bare/bundles/bareEncoding.js", "path": "/lib/bare/bundles/bareEncoding.js",
"keys": [ "keys": [
"bareEncoding" "bareEncoding"
] ]
}, },
{
"path": "/lib/bare/bundles/bareEvents.js",
"keys": [
"bareEvents"
]
},
{ {
"path": "/lib/bare/bundles/barePath.js", "path": "/lib/bare/bundles/barePath.js",
"keys": [ "keys": [
@@ -67,6 +67,18 @@
"bareAbortController" "bareAbortController"
] ]
}, },
{
"path": "/lib/bare/bundles/bareAnsiEscapes.js",
"keys": [
"bareAnsiEscapes"
]
},
{
"path": "/lib/bare/bundles/bareAddonResolve.js",
"keys": [
"bareAddonResolve"
]
},
{ {
"path": "/lib/bare/bundles/bareReadline.js", "path": "/lib/bare/bundles/bareReadline.js",
"keys": [ "keys": [
@@ -74,9 +86,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareAnsiEscapes.js", "path": "/lib/bare/bundles/bareAppKit.js",
"keys": [ "keys": [
"bareAnsiEscapes" "bareAppKit"
] ]
}, },
{ {
@@ -86,9 +98,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareAddonResolve.js", "path": "/lib/bare/bundles/bareApk.js",
"keys": [ "keys": [
"bareAddonResolve" "bareApk"
]
},
{
"path": "/lib/bare/bundles/bareAssert.js",
"keys": [
"bareAssert"
] ]
}, },
{ {
@@ -109,24 +127,6 @@
"fetch" "fetch"
] ]
}, },
{
"path": "/lib/bare/bundles/bareApk.js",
"keys": [
"bareApk"
]
},
{
"path": "/lib/bare/bundles/bareAssert.js",
"keys": [
"bareAssert"
]
},
{
"path": "/lib/bare/bundles/bareAppKit.js",
"keys": [
"bareAppKit"
]
},
{ {
"path": "/lib/bare/bundles/bareBmp.js", "path": "/lib/bare/bundles/bareBmp.js",
"keys": [ "keys": [
@@ -151,12 +151,6 @@
"bareBuffer" "bareBuffer"
] ]
}, },
{
"path": "/lib/bare/bundles/bareBundleEvaluate.js",
"keys": [
"bareBundleEvaluate"
]
},
{ {
"path": "/lib/bare/bundles/bareBluetoothApple.js", "path": "/lib/bare/bundles/bareBluetoothApple.js",
"keys": [ "keys": [
@@ -164,9 +158,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareBoot.js", "path": "/lib/bare/bundles/bareBundleEvaluate.js",
"keys": [ "keys": [
"bareBoot" "bareBundleEvaluate"
] ]
}, },
{ {
@@ -175,18 +169,18 @@
"bareConsole" "bareConsole"
] ]
}, },
{
"path": "/lib/bare/bundles/bareDebugLog.js",
"keys": [
"bareDebugLog"
]
},
{ {
"path": "/lib/bare/bundles/bareBundleId.js", "path": "/lib/bare/bundles/bareBundleId.js",
"keys": [ "keys": [
"bareBundleId" "bareBundleId"
] ]
}, },
{
"path": "/lib/bare/bundles/bareBoot.js",
"keys": [
"bareBoot"
]
},
{ {
"path": "/lib/bare/bundles/bareDaemon.js", "path": "/lib/bare/bundles/bareDaemon.js",
"keys": [ "keys": [
@@ -194,9 +188,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareDelta.js", "path": "/lib/bare/bundles/bareDebugLog.js",
"keys": [ "keys": [
"bareDelta" "bareDebugLog"
] ]
}, },
{ {
@@ -206,9 +200,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareDns.js", "path": "/lib/bare/bundles/bareDelta.js",
"keys": [ "keys": [
"bareDns" "bareDelta"
] ]
}, },
{ {
@@ -218,15 +212,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareEnv.js", "path": "/lib/bare/bundles/bareDns.js",
"keys": [ "keys": [
"bareEnv" "bareDns"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareExif.js", "path": "/lib/bare/bundles/bareEnv.js",
"keys": [ "keys": [
"bareExif" "bareEnv"
] ]
}, },
{ {
@@ -236,15 +230,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareDgram.js", "path": "/lib/bare/bundles/bareExif.js",
"keys": [ "keys": [
"bareDgram" "bareExif"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareFfmpegEncodings.js", "path": "/lib/bare/bundles/bareDgram.js",
"keys": [ "keys": [
"bareFfmpegEncodings" "bareDgram"
] ]
}, },
{ {
@@ -253,12 +247,24 @@
"bareFfmpeg" "bareFfmpeg"
] ]
}, },
{
"path": "/lib/bare/bundles/bareFfmpegEncodings.js",
"keys": [
"bareFfmpegEncodings"
]
},
{ {
"path": "/lib/bare/bundles/bareFormData.js", "path": "/lib/bare/bundles/bareFormData.js",
"keys": [ "keys": [
"bareFormData" "bareFormData"
] ]
}, },
{
"path": "/lib/bare/bundles/bareFileLogger.js",
"keys": [
"bareFileLogger"
]
},
{ {
"path": "/lib/bare/bundles/bareFormat.js", "path": "/lib/bare/bundles/bareFormat.js",
"keys": [ "keys": [
@@ -271,18 +277,6 @@
"bareGif" "bareGif"
] ]
}, },
{
"path": "/lib/bare/bundles/bareFileLogger.js",
"keys": [
"bareFileLogger"
]
},
{
"path": "/lib/bare/bundles/bareHeif.js",
"keys": [
"bareHeif"
]
},
{ {
"path": "/lib/bare/bundles/bareHrtime.js", "path": "/lib/bare/bundles/bareHrtime.js",
"keys": [ "keys": [
@@ -295,24 +289,18 @@
"bareGtk" "bareGtk"
] ]
}, },
{
"path": "/lib/bare/bundles/bareHeif.js",
"keys": [
"bareHeif"
]
},
{ {
"path": "/lib/bare/bundles/bareHttpParser.js", "path": "/lib/bare/bundles/bareHttpParser.js",
"keys": [ "keys": [
"bareHttpParser" "bareHttpParser"
] ]
}, },
{
"path": "/lib/bare/bundles/bareIco.js",
"keys": [
"bareIco"
]
},
{
"path": "/lib/bare/bundles/bareImageResample.js",
"keys": [
"bareImageResample"
]
},
{ {
"path": "/lib/bare/bundles/bareFs.js", "path": "/lib/bare/bundles/bareFs.js",
"keys": [ "keys": [
@@ -320,9 +308,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareHttp1.js", "path": "/lib/bare/bundles/bareImageResample.js",
"keys": [ "keys": [
"bareHttp1" "bareImageResample"
]
},
{
"path": "/lib/bare/bundles/bareIco.js",
"keys": [
"bareIco"
] ]
}, },
{ {
@@ -331,6 +325,12 @@
"bareInspect" "bareInspect"
] ]
}, },
{
"path": "/lib/bare/bundles/bareHttp1.js",
"keys": [
"bareHttp1"
]
},
{ {
"path": "/lib/bare/bundles/bareHttps.js", "path": "/lib/bare/bundles/bareHttps.js",
"keys": [ "keys": [
@@ -343,24 +343,12 @@
"bareJpeg" "bareJpeg"
] ]
}, },
{
"path": "/lib/bare/bundles/bareIpc.js",
"keys": [
"bareIpc"
]
},
{ {
"path": "/lib/bare/bundles/bareIntl.js", "path": "/lib/bare/bundles/bareIntl.js",
"keys": [ "keys": [
"bareIntl" "bareIntl"
] ]
}, },
{
"path": "/lib/bare/bundles/bareLief.js",
"keys": [
"bareLief"
]
},
{ {
"path": "/lib/bare/bundles/bareLogger.js", "path": "/lib/bare/bundles/bareLogger.js",
"keys": [ "keys": [
@@ -368,9 +356,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareInspector.js", "path": "/lib/bare/bundles/bareIpc.js",
"keys": [ "keys": [
"bareInspector" "bareIpc"
]
},
{
"path": "/lib/bare/bundles/bareLief.js",
"keys": [
"bareLief"
] ]
}, },
{ {
@@ -380,15 +374,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareMake.js", "path": "/lib/bare/bundles/bareInspector.js",
"keys": [ "keys": [
"bareMake" "bareInspector"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareModuleResolve.js", "path": "/lib/bare/bundles/bareMake.js",
"keys": [ "keys": [
"bareModuleResolve" "bareMake"
] ]
}, },
{ {
@@ -398,9 +392,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareModuleTraverse.js", "path": "/lib/bare/bundles/bareModuleResolve.js",
"keys": [ "keys": [
"bareModuleTraverse" "bareModuleResolve"
] ]
}, },
{ {
@@ -410,9 +404,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareNative.js", "path": "/lib/bare/bundles/bareModuleTraverse.js",
"keys": [ "keys": [
"bareNative" "bareModuleTraverse"
] ]
}, },
{ {
@@ -428,9 +422,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareMedia.js", "path": "/lib/bare/bundles/bareNative.js",
"keys": [ "keys": [
"bareMedia" "bareNative"
] ]
}, },
{ {
@@ -445,48 +439,36 @@
"bareOs" "bareOs"
] ]
}, },
{
"path": "/lib/bare/bundles/bareMedia.js",
"keys": [
"bareMedia"
]
},
{ {
"path": "/lib/bare/bundles/bareNet.js", "path": "/lib/bare/bundles/bareNet.js",
"keys": [ "keys": [
"bareNet" "bareNet"
] ]
}, },
{
"path": "/lib/bare/bundles/bareDev.js",
"keys": [
"bareDev"
]
},
{
"path": "/lib/bare/bundles/barePack.js",
"keys": [
"barePack"
]
},
{ {
"path": "/lib/bare/bundles/barePerformance.js", "path": "/lib/bare/bundles/barePerformance.js",
"keys": [ "keys": [
"barePerformance" "barePerformance"
] ]
}, },
{
"path": "/lib/bare/bundles/barePack.js",
"keys": [
"barePack"
]
},
{ {
"path": "/lib/bare/bundles/barePackDrive.js", "path": "/lib/bare/bundles/barePackDrive.js",
"keys": [ "keys": [
"barePackDrive" "barePackDrive"
] ]
}, },
{
"path": "/lib/bare/bundles/barePng.js",
"keys": [
"barePng"
]
},
{
"path": "/lib/bare/bundles/bareNodeRuntime.js",
"keys": [
"bareNodeRuntime"
]
},
{ {
"path": "/lib/bare/bundles/barePipe.js", "path": "/lib/bare/bundles/barePipe.js",
"keys": [ "keys": [
@@ -494,9 +476,21 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/barePrebuild.js", "path": "/lib/bare/bundles/barePng.js",
"keys": [ "keys": [
"barePrebuild" "barePng"
]
},
{
"path": "/lib/bare/bundles/bareDev.js",
"keys": [
"bareDev"
]
},
{
"path": "/lib/bare/bundles/bareNodeRuntime.js",
"keys": [
"bareNodeRuntime"
] ]
}, },
{ {
@@ -506,15 +500,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareQuerystring.js", "path": "/lib/bare/bundles/barePrebuild.js",
"keys": [ "keys": [
"bareQuerystring" "barePrebuild"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareProcess.js", "path": "/lib/bare/bundles/bareQuerystring.js",
"keys": [ "keys": [
"bareProcess" "bareQuerystring"
] ]
}, },
{ {
@@ -529,12 +523,24 @@
"bareRealm" "bareRealm"
] ]
}, },
{
"path": "/lib/bare/bundles/bareProcess.js",
"keys": [
"bareProcess"
]
},
{ {
"path": "/lib/bare/bundles/bareRuntime.js", "path": "/lib/bare/bundles/bareRuntime.js",
"keys": [ "keys": [
"bareRuntime" "bareRuntime"
] ]
}, },
{
"path": "/lib/bare/bundles/bareRpc.js",
"keys": [
"bareRpc"
]
},
{ {
"path": "/lib/bare/bundles/barePromClient.js", "path": "/lib/bare/bundles/barePromClient.js",
"keys": [ "keys": [
@@ -559,12 +565,6 @@
"bareRepl" "bareRepl"
] ]
}, },
{
"path": "/lib/bare/bundles/bareRpc.js",
"keys": [
"bareRpc"
]
},
{ {
"path": "/lib/bare/bundles/bareRun.js", "path": "/lib/bare/bundles/bareRun.js",
"keys": [ "keys": [
@@ -577,12 +577,6 @@
"bareSignals" "bareSignals"
] ]
}, },
{
"path": "/lib/bare/bundles/bareStorage.js",
"keys": [
"bareStorage"
]
},
{ {
"path": "/lib/bare/bundles/bareSidecar.js", "path": "/lib/bare/bundles/bareSidecar.js",
"keys": [ "keys": [
@@ -596,15 +590,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareStream.js", "path": "/lib/bare/bundles/bareStorage.js",
"keys": [ "keys": [
"bareStream" "bareStorage"
]
},
{
"path": "/lib/bare/bundles/bareStdio.js",
"keys": [
"bareStdio"
] ]
}, },
{ {
@@ -614,9 +602,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareTap.js", "path": "/lib/bare/bundles/bareStream.js",
"keys": [ "keys": [
"bareTap" "bareStream"
]
},
{
"path": "/lib/bare/bundles/bareStdio.js",
"keys": [
"bareStdio"
] ]
}, },
{ {
@@ -632,9 +626,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareSubprocess.js", "path": "/lib/bare/bundles/bareTap.js",
"keys": [ "keys": [
"bareSubprocess" "bareTap"
] ]
}, },
{ {
@@ -644,9 +638,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareTimers.js", "path": "/lib/bare/bundles/bareSubprocess.js",
"keys": [ "keys": [
"bareTimers" "bareSubprocess"
] ]
}, },
{ {
@@ -655,18 +649,24 @@
"bareThread" "bareThread"
] ]
}, },
{
"path": "/lib/bare/bundles/bareTcp.js",
"keys": [
"bareTcp"
]
},
{ {
"path": "/lib/bare/bundles/bareTpl.js", "path": "/lib/bare/bundles/bareTpl.js",
"keys": [ "keys": [
"bareTpl" "bareTpl"
] ]
}, },
{
"path": "/lib/bare/bundles/bareTimers.js",
"keys": [
"bareTimers"
]
},
{
"path": "/lib/bare/bundles/bareTcp.js",
"keys": [
"bareTcp"
]
},
{ {
"path": "/lib/bare/bundles/bareType.js", "path": "/lib/bare/bundles/bareType.js",
"keys": [ "keys": [
@@ -703,18 +703,18 @@
"bareV8" "bareV8"
] ]
}, },
{
"path": "/lib/bare/bundles/bareWalkHandles.js",
"keys": [
"bareWalkHandles"
]
},
{ {
"path": "/lib/bare/bundles/bareVm.js", "path": "/lib/bare/bundles/bareVm.js",
"keys": [ "keys": [
"bareVm" "bareVm"
] ]
}, },
{
"path": "/lib/bare/bundles/bareWalkHandles.js",
"keys": [
"bareWalkHandles"
]
},
{ {
"path": "/lib/bare/bundles/bareUnionBundle.js", "path": "/lib/bare/bundles/bareUnionBundle.js",
"keys": [ "keys": [
@@ -746,9 +746,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareWinUi.js", "path": "/lib/bare/bundles/bareUtils.js",
"keys": [ "keys": [
"bareWinUi" "bareUtils"
] ]
}, },
{ {
@@ -758,15 +758,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareXdiff.js", "path": "/lib/bare/bundles/bareWinUi.js",
"keys": [ "keys": [
"bareXdiff" "bareWinUi"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareUtils.js", "path": "/lib/bare/bundles/bareXdiff.js",
"keys": [ "keys": [
"bareUtils" "bareXdiff"
] ]
}, },
{ {
@@ -776,9 +776,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareWorker.js", "path": "/lib/bare/bundles/bareZmq.js",
"keys": [ "keys": [
"bareWorker" "bareZmq"
] ]
}, },
{ {
@@ -788,9 +788,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareZmq.js", "path": "/lib/bare/bundles/bareWorker.js",
"keys": [ "keys": [
"bareZmq" "bareWorker"
] ]
} }
], ],
File diff suppressed because one or more lines are too long