Rename minimal shell build to base
ober
4b04b621b3e62fd3b8f09bccd0c6269a85bf8953
--- a/.gitignore +++ b/.gitignore @@ -13,8 +13,8 @@ /jsh-android.sha256 /jsh-macos /jsh-macos.sha256 -/jsh-macos-minimal -/jsh-macos-minimal.sha256 +/jsh-macos-base +/jsh-macos-base.sha256 /jsh-macos-full /jsh-macos-full.sha256 /jsh-freebsd --- a/Makefile +++ b/Makefile @@ -64,9 +64,9 @@ PODMAN ?= podman .PHONY: ensure-jerboa-tools ensure-jsqlite stage-jsqlite ffi jerboa compile run clean test binary all jsh jsh-compile jsh-run jsh-binary jsh-jerbuild \ musl test-binary test-mux test-mux-screen \ gen-embed embed-crypto gen-certs verify-harden help check-platforms install-hooks \ - jsh-macos jsh-macos-minimal jsh-macos-full macos \ - jsh-freebsd jsh-freebsd-minimal jsh-freebsd-full \ - jsh-android jsh-android-minimal jsh-android-full android \ + jsh-macos jsh-macos-base jsh-macos-full macos \ + jsh-freebsd jsh-freebsd-base jsh-freebsd-full \ + jsh-android jsh-android-base jsh-android-full android \ linux linux-amd64 linux-arm64 jsh-linux-amd64 jsh-linux-arm64 test-linux test-linux-amd64 \ rust-musl-target-amd64 rust-musl-target-arm64 chez-cross-amd64 chez-cross-arm64 \ rust-coreutils-linux-amd64 rust-coreutils-linux-arm64 jerboa-native-linux-amd64 jerboa-native-linux-arm64 \ @@ -322,7 +322,7 @@ jsh-freebsd: ensure-jerboa-tools ensure-jsqlite ffi-shim-symbols.list vendor-dep @echo "=== Building jsh for FreeBSD with jerbuild ===" JERBUILD="$(JERBUILD)" JSH_FEATURES="$(JSH_FEATURES)" $(JERBUILD) build --config .jerbuild.freebsd-amd64 -jsh-freebsd-minimal: +jsh-freebsd-base: @$(MAKE) jsh-freebsd JSH_FEATURES=none jsh-freebsd-full: @@ -350,10 +350,10 @@ jsh-jerbuild: ensure-jerboa-tools ensure-jsqlite ffi-shim-symbols.list vendor-de jsh-macos: jsh-jerbuild -jsh-macos-minimal: +jsh-macos-base: @$(MAKE) jsh-jerbuild JSH_FEATURES=none - @cp -f jsh-macos jsh-macos-minimal - @shasum -a 256 jsh-macos-minimal > jsh-macos-minimal.sha256 + @cp -f jsh-macos jsh-macos-base + @shasum -a 256 jsh-macos-base > jsh-macos-base.sha256 jsh-macos-full: @$(MAKE) jsh-jerbuild JSH_FEATURES=all @@ -619,7 +619,7 @@ jsh-android: ensure-jerboa-tools ffi-shim-symbols.list vendor-deps jerboa vendor @echo "=== Building jsh on Android/Termux ===" JSH_FEATURES="$(JSH_FEATURES)" bash build-jsh-android.sh -jsh-android-minimal: +jsh-android-base: @$(MAKE) jsh-android JSH_FEATURES=none jsh-android-full: @@ -865,13 +865,13 @@ help: @echo " make linux-amd64 Cross: linux/amd64 static ELF (Chez xpatch + musl-cross, no Podman)" @echo " make linux-arm64 Cross: linux/arm64 static ELF (Chez xpatch + musl-cross, no Podman)" @echo " make freebsd-amd64 freebsd/amd64 (native on a FreeBSD amd64 host)" - @echo " make jsh-macos-minimal macOS minimal build (core shell only)" + @echo " make jsh-macos-base macOS base build (core shell only)" @echo " make jsh-macos-full macOS full build (all features)" @echo " make jsh-freebsd FreeBSD binary via jerbuild" - @echo " make jsh-freebsd-minimal FreeBSD minimal build (core shell only)" + @echo " make jsh-freebsd-base FreeBSD base build (core shell only)" @echo " make jsh-freebsd-full FreeBSD full build (all features)" @echo " make android Android/Termux (native on a Termux device)" - @echo " make jsh-android-minimal Android minimal build (core shell only)" + @echo " make jsh-android-base Android base build (core shell only)" @echo " make jsh-android-full Android full build (all features)" @echo " make linux Alias for linux-amd64" @echo " make test-linux-amd64 (optional) smoke-run jsh-linux-amd64 under alpine via Podman" @@ -988,7 +988,7 @@ clean: find src -name "*.so" -delete 2>/dev/null || true find src -name "*.wpo" -delete 2>/dev/null || true find src \( -name "*.ta6le" -o -name "*.ta6fb" -o -name "*.tarm64osx" -o -name "*.tx86_64osx" \) -delete 2>/dev/null || true - rm -f libjsh-ffi.so libjsh-ffi.dylib libjsh-ffi-musl.so libembed-crypto.so libcoreutils.so libcoreutils.dylib jsh-all.so jsh.wpo jsh.boot jsh jsh-musl jsh-musl.sha256 jsh-freebsd jsh-freebsd.sha256 jsh-macos jsh-macos.sha256 jsh-macos-minimal jsh-macos-minimal.sha256 jsh-macos-full jsh-macos-full.sha256 ffi-shim-symbols.list + rm -f libjsh-ffi.so libjsh-ffi.dylib libjsh-ffi-musl.so libembed-crypto.so libcoreutils.so libcoreutils.dylib jsh-all.so jsh.wpo jsh.boot jsh jsh-musl jsh-musl.sha256 jsh-freebsd jsh-freebsd.sha256 jsh-macos jsh-macos.sha256 jsh-macos-base jsh-macos-base.sha256 jsh-macos-full jsh-macos-full.sha256 ffi-shim-symbols.list rm -f jsh_program.h jsh_petite_boot.h jsh_scheme_boot.h jsh_jsh_boot.h rm -f jsh-linux-amd64 jsh-linux-amd64-main.c jsh-linux-amd64.wp.so rm -f jsh-linux-arm64 jsh-linux-arm64-main.c jsh-linux-arm64.wp.so --- a/asciinema.md +++ b/asciinema.md @@ -121,7 +121,7 @@ while jerboa-aware tools can reconstruct full session semantics. (recorder-resize! cols rows) ; shorthand for "r" event ``` -#### Integration Points (minimal patches) +#### Integration Points (base patches) 1. **lineedit.ss**: After each raw byte read, `(when (*recording?*) (recorder-input! byte))` 2. **executor.ss**: Before dispatch, emit "c" event. After completion, emit "x" event. --- a/build-jerboa.ss +++ b/build-jerboa.ss @@ -1848,7 +1848,7 @@ ;; (hand-written file, not auto-generated — no patch needed here) ;; 14. Add --eval flag to sandbox builtin for restricted eval - ;; Safe eval with a minimal binding allowlist + ;; Safe eval with a base binding allowlist (patch-file! "src/jsh/main.sls" " (fprintf (current-error-port)\n \" -c CMD run command~n\")" (string-append --- a/build-jsh-android.ss +++ b/build-jsh-android.ss @@ -190,7 +190,7 @@ ;; ========== Feature resolution ========== ;; Derive *enabled-features* from JSH_FEATURES env var. -;; ""/"none" → '() (minimal build) +;; ""/"none" → '() (base build) ;; "all" → all known optional features ;; "foo,bar" → '(foo bar) @@ -227,7 +227,7 @@ 'replace)))) (printf "[1/7] Compiling jsh-generated.ss (~a, optimize-level 3)...~n" - (if (null? *enabled-features*) "minimal" "full")) + (if (null? *enabled-features*) "base" "full")) (with-warnings-suppressed (lambda () (parameterize ([compile-imported-libraries #t] --- a/build-jsh-cross.ss +++ b/build-jsh-cross.ss @@ -56,7 +56,7 @@ ;; Feature resolution — derive *enabled-features* from JSH_FEATURES env var. ;; JERBOA_FEATURES is accepted as a compatibility alias for CLI ergonomics. -;; ""/"none" → '() (minimal build) +;; ""/"none" → '() (base build) ;; "all" → all known optional features ;; "foo,bar" → '(foo bar) (define cross-supported-all-features @@ -621,10 +621,10 @@ ;; jsh.ss carries a placeholder `(define *jsh-enabled-features* '())`. ;; jsh-generate.ss rewrites that line based on *enabled-features* so the ;; runtime `,features` command reports what was actually built. Without -;; this step the cross-build would always report "minimal" regardless of +;; this step the cross-build would always report "base" regardless of ;; JSH_FEATURES. (printf "==> [2/6] generate-jsh-program (features: ~a)~n" - (if (null? *enabled-features*) "minimal" "full")) + (if (null? *enabled-features*) "base" "full")) (load "features.def") (load "jsh-generate.ss") (generate-jsh-program *enabled-features*) --- a/build-jsh-freebsd-cross.ss +++ b/build-jsh-freebsd-cross.ss @@ -33,7 +33,7 @@ (define ffi-shim "ffi-shim.c") ;; Feature resolution — derive *enabled-features* from JSH_FEATURES env var. -;; ""/"none" → '() (minimal build) +;; ""/"none" → '() (base build) ;; "all" → all known optional features ;; "foo,bar" → '(foo bar) (define *enabled-features* @@ -128,7 +128,7 @@ ;; jsh-generate.ss rewrites that line based on *enabled-features* so the ;; runtime `,features` command reports what was actually built. (printf "==> [2/6] generate-jsh-program (features: ~a)~n" - (if (null? *enabled-features*) "minimal" "full")) + (if (null? *enabled-features*) "base" "full")) (load "features.def") (load "jsh-generate.ss") (generate-jsh-program *enabled-features*) --- a/build-jsh-freebsd.ss +++ b/build-jsh-freebsd.ss @@ -620,7 +620,7 @@ ;; ========== Feature resolution ========== ;; Derive *enabled-features* from JSH_FEATURES env var. -;; ""/"none" → '() (minimal build) +;; ""/"none" → '() (base build) ;; "all" → all known optional features ;; "foo,bar" → '(foo bar) @@ -656,7 +656,7 @@ (generate-jsh-program *enabled-features*) (printf "~n[2/7] Compiling jsh-generated.ss (~a, optimize-level 3)...~n" - (if (null? *enabled-features*) "minimal" "full")) + (if (null? *enabled-features*) "base" "full")) (parameterize ([compile-imported-libraries #t] [optimize-level 3] [cp0-effort-limit 500] --- a/build-jsh-macos.ss +++ b/build-jsh-macos.ss @@ -103,7 +103,7 @@ ;; ========== Feature resolution ========== ;; Derive *enabled-features* from JSH_FEATURES env var. -;; ""/"none" → '() (minimal build) +;; ""/"none" → '() (base build) ;; "all" → all known optional features ;; "foo,bar" → '(foo bar) --- a/build-jsh-musl.ss +++ b/build-jsh-musl.ss @@ -515,7 +515,7 @@ ;; ========== Feature resolution ========== ;; Derive *enabled-features* from JSH_FEATURES env var. -;; ""/"none" → '() (minimal build) +;; ""/"none" → '() (base build) ;; "all" → all known optional features ;; "foo,bar" → '(foo bar) @@ -551,7 +551,7 @@ (generate-jsh-program *enabled-features*) (printf "~n[2/7] Compiling jsh-generated.ss (~a, optimize-level 3)...~n" - (if (null? *enabled-features*) "minimal" "full")) + (if (null? *enabled-features*) "base" "full")) ;; compile-imported-libraries is needed so compile-program can resolve ;; imports from (jsh registry) etc. The .so files from step 1 already ;; exist, so Chez will use them rather than recompiling. --- a/docs/capability-wrapper-followup.md +++ b/docs/capability-wrapper-followup.md @@ -19,7 +19,7 @@ Status updated: 2026-05-22 (Linux Landlock + cross-binary follow-up) | 10 | Tracefs | SAFE PARTIAL — Linux `strace` wrapper is invoked when available; Jerboa now provides normalized `fs_event` records, best-effort fd/cwd tracking, and read/write/exec suggestions; otherwise fail closed; native backends remain future work | | 11 | Audit output depth | PARTIAL — process/policy/fs/net/shim/refusal/limit-install/limit-supervision/tracefs/workspace/sandbox-install events + argv redaction; **GAP**: exact process tree and redacted stdout/stderr summaries | | 12 | Phase/help text | DONE — stale "(parse/explain only)" wording removed; `docs/meta-commands.md` documents policy status | -| 13 | Full binary artifacts | PARTIAL — macOS primary/minimal/full targets rebuild as distinct artifacts; Linux amd64 static cross artifact builds and passes policy/broad suites; musl Podman is blocked by qemu/rustc SIGSEGV; FreeBSD/Android rebuilds remain | +| 13 | Full binary artifacts | PARTIAL — macOS primary/base/full targets rebuild as distinct artifacts; Linux amd64 static cross artifact builds and passes policy/broad suites; musl Podman is blocked by qemu/rustc SIGSEGV; FreeBSD/Android rebuilds remain | | 14 | Tests that must be added | DONE — policy binary suite has macOS/Linux coverage; unit, macOS broad, and Linux broad suites are clean | The detailed requirement lists in §§1–14 below preserve the original asks, with @@ -91,7 +91,7 @@ resolution. `jsh-macos` was rebuilt locally in this pass with SHA-256 `f7ab45ae5d32285d15e2cd5900fd2bd86d33af3b851c45ea9eea8e7e7114b9c5`. -`jsh-macos-minimal` and `jsh-macos-full` were not executable in this working +`jsh-macos-base` and `jsh-macos-full` were not executable in this working tree during the policy command smoke coverage. Optional recording, SSH, AWS, and cross-build feature blocks are feature-aware and skip when the tested binary reports those features disabled. @@ -665,14 +665,14 @@ Required work: 2. Update `docs/meta-commands.md` to document policy commands. 3. Document which commands are enforcing, partially enforcing, or parse-only. 4. Make `,help` feature-aware: - - minimal build should say sandbox/rl policy axes are unenforced + - base build should say sandbox/rl policy axes are unenforced - full build should say which features are available ## 13. Full Binary Artifacts Are Stale Or Inconsistent Observed: -- `./jsh-macos` recognized the new policy commands but was a minimal build. +- `./jsh-macos` recognized the new policy commands but was a base build. - `./jsh-macos-full` listed many features but did not recognize the new policy commands during review. @@ -680,7 +680,7 @@ Required work: 1. Rebuild all release artifacts after policy changes: - `jsh-macos` — rebuilt and committed - - `jsh-macos-minimal` — rebuilt locally via named-output target + - `jsh-macos-base` — rebuilt locally via named-output target - `jsh-macos-full` — rebuilt locally via named-output target - `jsh-linux-amd64` — rebuilt with the macOS-to-Linux static cross path and tested under Ubuntu amd64/podman --- a/docs/musl-build.md +++ b/docs/musl-build.md @@ -119,7 +119,7 @@ podman run -v $PWD:/work -w /work docker.io/library/alpine:latest ./jsh-linux-am # Ubuntu podman run -v $PWD:/work -w /work docker.io/library/ubuntu:latest ./jsh-linux-amd64 -c 'echo Works on Ubuntu' -# FROM scratch (minimal container) +# FROM scratch (base container) podman run -v $PWD:/work -w /work scratch ./jsh-linux-amd64 -c 'echo Works in scratch' ``` --- a/docs/performance-overview.md +++ b/docs/performance-overview.md @@ -6,7 +6,7 @@ Environment: - Host: macOS 15.5 arm64. - Benchmark: `~/mine/shellbench/shellbench -t 1 -w 0`. -- Shells: `/bin/bash`, `jsh-macos-minimal` (`JSH_FEATURES=none`), `jsh-macos-full` (`JSH_FEATURES=all`). +- Shells: `/bin/bash`, `jsh-macos-base` (`JSH_FEATURES=none`), `jsh-macos-full` (`JSH_FEATURES=all`). - Final raw output: `_tmp/shellbench/macos-final.txt`. - Focused Rust `cut` reruns after the fork fix: `_tmp/shellbench/repro-cut-pipeline-then-heredoc-final.txt`, @@ -19,7 +19,7 @@ No changes were made in `~/mine/jerboa/`. ## Correctness fixes from this pass -1. Linux/macOS minimal no longer registers coreutils weak stubs when the +1. Linux/macOS base no longer registers coreutils weak stubs when the `coreutils` feature is disabled. That fixed the `command -v ls` result like `[jsh-linux-amd64-weak] jsh_ls` in `JSH_FEATURES=none` builds. @@ -41,7 +41,7 @@ No changes were made in `~/mine/jerboa/`. 5. Parameter expansion now uses the faster public Jerboa string APIs for common literal cases: `(std misc string)` for contains/prefix/suffix checks and `(std misc string-more)` for all-literal replacement. The macOS static boot - lists now include `std/misc/string-more`; without that, the minimal binary + lists now include `std/misc/string-more`; without that, the base binary built but failed at runtime when loading the new import. ## Final shellbench highlights --- a/expander.ss +++ b/expander.ss @@ -1288,7 +1288,7 @@ ;; one char. Returns the index of the separator slash, or #f if not found. (def (find-patsub-separator str pattern-start) (let ((len (string-length str))) - ;; First, skip one unit (char or quoted group) to consume the minimal pattern + ;; First, skip one unit (char or quoted group) to consume the base pattern (let ((past-first (cond ((>= pattern-start len) pattern-start) --- a/feature-resolve.ss +++ b/feature-resolve.ss @@ -38,7 +38,7 @@ "Display an interactive checkbox prompt on /dev/tty. Returns list of enabled feature symbols." (let* ([names (feature-names)] [n (length names)] - [selected (make-vector n #f)] ;; all off by default = minimal + [selected (make-vector n #f)] ;; all off by default = base [tty-in (open-input-file "/dev/tty")] [tty-out (open-output-file "/dev/tty" '(no-fail no-create no-truncate))]) @@ -124,7 +124,7 @@ all)] ;; CI override: none / empty [(and env (or (string=? env "none") (string=? env ""))) - (printf "Features: minimal (core only)~n") + (printf "Features: base (core only)~n") '()] ;; CI override: comma-separated list [(and env (> (string-length env) 0)) @@ -140,12 +140,12 @@ [(and (not env) (file-exists? "/dev/tty")) (let ([selected (interactive-feature-select)]) (if (null? selected) - (printf "Features: minimal (core only)~n") + (printf "Features: base (core only)~n") (printf "Features: ~{~a ~}~n" selected)) selected)] - ;; Batch mode, no env var, no TTY → minimal + ;; Batch mode, no env var, no TTY -> base [else - (printf "Features: minimal (core only, no TTY for selection)~n") + (printf "Features: base (core only, no TTY for selection)~n") '()]))) ;; ─── Query helpers (used by build scripts after resolution) ────────────────── new file mode 100644 --- /dev/null +++ b/features.conf.base @@ -0,0 +1,3 @@ +;; features.conf.base — Core shell only, no extras +;; Copy to features.conf to use: cp features.conf.base features.conf +(features) deleted file mode 100644 --- a/features.conf.minimal +++ /dev/null @@ -1,3 +0,0 @@ -;; features.conf.minimal — Core shell only, no extras -;; Copy to features.conf to use: cp features.conf.minimal features.conf -(features) --- a/jerboa-src/lib/std/os/limits/sandbox.ss +++ b/jerboa-src/lib/std/os/limits/sandbox.ss @@ -24,7 +24,7 @@ ;;; net 'allow | 'deny | 'local-only | 'allowlist ;;; net-allow list of host:port for 'allowlist ;;; net-connect-ports list of TCP destination ports allowed by Landlock -;;; syscalls 'unrestricted | 'safe | 'minimal (Linux seccomp hint) +;;; syscalls 'unrestricted | 'safe | 'compute-only (Linux seccomp hint) ;;; ptrace? whether the child may be ptraced (defaults #f) ;;; no-new-privs? whether to set PR_SET_NO_NEW_PRIVS (Linux only) ;;; @@ -557,7 +557,7 @@ ;; 'safe (default) → default-allow blocklist of dangerous syscalls ;; (ptrace, kernel-module/kexec, bpf, perf, mount, …) ;; returning EPERM — robust for arbitrary programs. - ;; 'minimal → strict compute-only allowlist (KILL on violation); + ;; 'compute-only → strict compute-only allowlist (KILL on violation); ;; may break programs needing syscalls outside that set. (let ([mode (sandbox-policy-get pol 'syscalls)]) (cond @@ -568,7 +568,7 @@ (begin (seccomp-install! (case mode - [(minimal) compute-only-filter] + [(compute-only) compute-only-filter] [else safe-blocklist])) 'installed) (catch (e) 'degraded))]))) --- a/jerboa-src/src/jsh/mux-transport.ss +++ b/jerboa-src/src/jsh/mux-transport.ss @@ -681,7 +681,7 @@ ;; Carries mux protocol inside WebSocket binary frames (RFC 6455). ;; Server->client frames are unmasked; client->server frames are masked. -;; Minimal WebSocket binary frame encoder. +;; Base WebSocket binary frame encoder. ;; Returns a bytevector containing the frame header + payload. (define (ws-encode-binary-frame payload masked?) (let* ([plen (bytevector-length payload)] --- a/jerboa-src/src/jsh/worm.ss +++ b/jerboa-src/src/jsh/worm.ss @@ -1,7 +1,7 @@ ;;; (jsh worm) -- jerboa-wormhole comma command integration. ;;; ;;; Registers ",worm" with the meta-command registry. The actual wormhole -;;; implementation is loaded lazily from (wormhole cli), so minimal builds can +;;; implementation is loaded lazily from (wormhole cli), so base builds can ;;; still compile and report a clean "not available" error. ;;; jerbuild-library: (jsh worm) --- a/jerboa-src/vault-stage/chez/fuse/access.ss +++ b/jerboa-src/vault-stage/chez/fuse/access.ss @@ -114,7 +114,7 @@ ;; to unauthorized processes. No error codes — just... nothing there. (define (stealth-deny-attr) - ;; Return a minimal directory attr for the root node. + ;; Return a base directory attr for the root node. ;; This makes the mountpoint itself appear to exist (it must, since it's ;; a mount point) but contain nothing. (let ([now (time-second (current-time))]) new file mode 100644 --- /dev/null +++ b/jsh-base.ss @@ -0,0 +1,446 @@ +#!chezscheme +;; jsh-base.ss — Core-only jsh program (no optional features) +;; +;; This is the baseline program for builds with zero features enabled. +;; It provides: shell execution, ,help, ,features, ,room, ,out, ,use, ,<expr> +;; No coreutils, mux, ssh, vault, aws, record, sandbox, cage, rl, profiler, proxy. + +(import (chezscheme) (jsh main) (except (jsh builtins) list-head) + (jsh registry) (jsh script) + (only (jsh executor) ast->command-text) + (only (jsh environment) *command-trace-hook* env-exported-alist) + (only (jsh parser) parse-complete-command) + (only (jsh ast) ast-pipeline? ast-pipeline-commands ast-pipeline-bang? + simple-command? simple-command-assignments simple-command-words + assignment? assignment-name assignment-value assignment-op) + (only (jsh embed) embed-encrypted? embed-unlocked?) + (only (jsh ffi) ffi-termios-restore ffi-termios-save ffi-set-raw-mode + ffi-fork-exec ffi-nanosleep-us ffi-byte-ready? ffi-read-byte + ffi-fdwrite ffi-terminal-columns ffi-terminal-rows + ffi-signal-flag-install ffi-signal-flag-check + ffi-dup ffi-dup2 ffi-close-fd ffi-open-raw ffi-getpid + ffi-self-exe-path ffi-process-alive ffi-read-pidfile + ffi-fork ffi-exit ffi-setsid ffi-ensure-std-fds + O_WRONLY O_CREAT O_TRUNC) + (only (jsh util) expand-tilde) + (only (jsh history) history-add-raw! history-entries history-clear! + history-entry-timestamp history-entry-cwd history-entry-command + *history-save-override*) + (only (compiler compile) jerboa-compile-top) + (only (reader reader) jerboa-read) + (only (std os path) path-strip-directory path-directory) + ) + +;; ── Feature manifest (base = empty) ── +(define *jsh-enabled-features* '()) + +;; ── Session state ── + +(define *jsh-unlocked* #f) +(define *jsh-privacy-mode* #f) + +(define (update-prompt-suffix!) + (*jsh-prompt-suffix* + (let* ([e (embed-encrypted?)] + [u *jsh-unlocked*] + [lock-part (cond [(and e u) "🔑"] [e "🔒"] [else ""])]) + (if (string=? lock-part "") "" (string-append " " lock-part))))) + +(define (sync-history-enabled!) + (*jsh-history-enabled* (not *jsh-privacy-mode*))) + +;; ── Tier setup ── + +(*jsh-tier* "small") +;; Force (jsh builtins) registration side effects +(let () special-builtin? (void)) + +;; ── String helpers ── + +(define (output-to-string proc) + (let ([p (open-output-string)]) + (proc p) + (get-output-string p))) + +(define (string-trim-whitespace s) + (let* ([len (string-length s)] + [start (let lp ([i 0]) + (if (and (< i len) (char-whitespace? (string-ref s i))) + (lp (+ i 1)) i))] + [end (let lp ([i len]) + (if (and (> i start) (char-whitespace? (string-ref s (- i 1)))) + (lp (- i 1)) i))]) + (substring s start end))) + +(define (string-prefix? prefix str) + (and (>= (string-length str) (string-length prefix)) + (string=? (substring str 0 (string-length prefix)) prefix))) + +(define (simple-tokenize str) + (let ([n (string-length str)]) + (let lp ([i 0] [tokens '()]) + (cond + ((>= i n) (reverse tokens)) + ((char-whitespace? (string-ref str i)) + (lp (+ i 1) tokens)) + ((char=? (string-ref str i) #\") + (let qloop ([j (+ i 1)] [chars '()]) + (cond + ((>= j n) + (reverse (cons (list->string (reverse chars)) tokens))) + ((char=? (string-ref str j) #\") + (lp (+ j 1) (cons (list->string (reverse chars)) tokens))) + (else + (qloop (+ j 1) (cons (string-ref str j) chars)))))) + ((char=? (string-ref str i) #\') + (let qloop ([j (+ i 1)] [chars '()]) + (cond + ((>= j n) + (reverse (cons (list->string (reverse chars)) tokens))) + ((char=? (string-ref str j) #\') + (lp (+ j 1) (cons (list->string (reverse chars)) tokens))) + (else + (qloop (+ j 1) (cons (string-ref str j) chars)))))) + (else + (let wloop ([j i]) + (if (or (>= j n) (char-whitespace? (string-ref str j)) + (char=? (string-ref str j) #\") (char=? (string-ref str j) #\')) + (lp j (cons (substring str i j) tokens)) + (wloop (+ j 1))))))))) + +(define (simple-join lst sep) + (if (null? lst) "" + (let loop ([rest (cdr lst)] [acc (car lst)]) + (if (null? rest) acc + (loop (cdr rest) (string-append acc sep (car rest))))))) + +(define (meta-command-like? expr-str) + (let ([s (string-trim-whitespace expr-str)]) + (and (> (string-length s) 0) + (let ([ch (string-ref s 0)]) + (or (char-alphabetic? ch) + (char=? ch #\_)))))) + +(define (unknown-meta-command expr-str) + (let* ([tokens (simple-tokenize expr-str)] + [name (if (null? tokens) expr-str (car tokens))]) + (fprintf (current-error-port) + "jsh: unknown meta-command ',~a' (try ,help)~n" + name) + (cons "" 2))) + +;; ── Jerboa reader / eval ── + +(define (jerboa-read-all port) + (let lp ([forms '()]) + (let ([datum (jerboa-read port)]) + (if (eof-object? datum) + (reverse forms) + (lp (cons datum forms)))))) + +(define (jerboa-read-all-from-string str) + (jerboa-read-all (open-input-string str))) + +(define *jerboa-env-ready* #f) + +(define (ensure-jerboa-env!) + (unless *jerboa-env-ready* + (let ([env (interaction-environment)]) + (define (import-lib! lib) + (eval `(import ,lib) env)) + (define (try-import-lib! lib) + (guard (e [#t #f]) + (import-lib! lib) + #t)) + (for-each + import-lib! + '((jerboa runtime) + (std error) + (std misc string) + (std misc list) + (std text glob) + (std os path) + (std format) + (std sort) + (std pregexp))) + (for-each + try-import-lib! + '((std misc lru-cache) + (std misc trie))) + (define-top-level-value '|jsh:run-cmd| run-cmd (interaction-environment)) + (define-top-level-value '|jsh:run-script| run-script (interaction-environment)) + (eval '(define run-cmd |jsh:run-cmd|) env) + (eval '(define run-script |jsh:run-script|) env) + ;; Gambit f64vector shims + (eval '(define (make-f64vector n . rest) + (if (null? rest) (make-flvector n) + (make-flvector n (car rest)))) env) + (eval '(define f64vector-ref flvector-ref) env) + (eval '(define f64vector-set! flvector-set!) env) + ;; Gambit ##process-statistics shim + (eval `(define (,(string->symbol "##process-statistics")) + (let* ([v (make-flvector 20 0.0)] + [stats (statistics)] + [gc-real (sstats-gc-real stats)] + [gc-real-secs (+ (time-second gc-real) + (/ (time-nanosecond gc-real) 1e9))] + [heap (exact->inexact (current-memory-bytes))] + [alloc (exact->inexact (bytes-allocated))]) + (flvector-set! v 0 (/ (cpu-time) 1000.0)) + (flvector-set! v 2 (/ (real-time) 1000.0)) + (flvector-set! v 5 gc-real-secs) + (flvector-set! v 6 (exact->inexact (collections))) + (flvector-set! v 7 heap) + (flvector-set! v 15 alloc) + (flvector-set! v 17 heap) + v)) env) + ;; Gambit threading shims + (eval '(import (only (std misc thread) + make-thread thread-start! thread-join! + thread-yield! thread-sleep! + current-thread thread-name thread? + thread-send thread-receive)) env) + ;; Gambit SMP primitives + (eval `(define (,(string->symbol "##set-parallelism-level!") n) (void)) env) + (eval `(define (,(string->symbol "##startup-parallelism!")) (void)) env) + (eval `(define ,(string->symbol "##current-vm-processor-count") + (let ([count ,(let ([c-sysconf (foreign-procedure "sysconf" (int) long)]) + (let ([result (c-sysconf 84)]) + (if (> result 0) result 1)))]) + (lambda () count))) env) + ;; Gambit I/O shims + (eval '(define (force-output . args) + (flush-output-port + (if (null? args) (current-output-port) (car args)))) env) + (set! *jerboa-env-ready* #t) + (void)))) + +(define (jerboa-eval-forms jerboa-forms) + (ensure-jerboa-env!) + (let ([env (interaction-environment)]) + (let loop ([fs jerboa-forms] [last (void)]) + (if (null? fs) last + (let ([form (car fs)]) + (cond + ((and (pair? form) (eq? (car form) 'export)) + (loop (cdr fs) last)) + ((and (pair? form) (eq? (car form) 'import)) + (loop (cdr fs) last)) + (else + (let ([chez-form (jerboa-compile-top form)]) + (loop (cdr fs) (eval chez-form env)))))))))) + +(define (format-result result) + (cond + [(eq? result (void)) ""] + [(or (pair? result) (vector? result)) + (output-to-string (lambda (port) (pretty-print result port)))] + [else + (output-to-string (lambda (port) (write result port)))])) + +;; ,use file.ss — compile and load a Jerboa source file +(define (handle-use-command path-str) + (let ([path (string-trim-whitespace path-str)]) + (unless (file-exists? path) + (error 'use (string-append "file not found: " path))) + (let* ([port (open-input-file path)] + [forms (jerboa-read-all port)]) + (close-input-port port) + (let ([result (jerboa-eval-forms forms)]) + (fprintf (current-error-port) "loaded: ~a (~a forms)~n" + path (length forms)) + result)))) + +;; ── Meta-command handler ── + +(*meta-command-handler* + (lambda (expr-str) + (guard (exn + [#t (cons + (output-to-string + (lambda (port) + (display "Jerboa error: " port) + (display-condition exn port))) + 1)]) + (cond + ;; Registry-based dispatch — packages self-register via meta-register! + [(let ([cmd (car (simple-tokenize expr-str))]) + (and (not (string=? cmd "")) (meta-lookup cmd))) + => (lambda (handler) + (let* ([tokens (simple-tokenize expr-str)] + [args (if (null? (cdr tokens)) "" + (string-trim-whitespace + (substring expr-str (string-length (car tokens)) + (string-length expr-str))))]) + (handler args)))] + ;; ,help / ,h / ,? + [(or (string=? expr-str "help") (string=? expr-str "h") (string=? expr-str "?")) + (let ([lines + '("jsh meta-commands (base core):" + " ,help ,h ,? show this help" + " ,features show enabled features" + " ,room GC & heap report" + " ,room #t verbose report with all object types" + " ,out [-o file] <cmd> capture stdout+stderr to file" + " ,use <file.ss> compile and load a Jerboa source file" + " ,<expr> evaluate a Jerboa Scheme expression")]) + (for-each (lambda (l) (display l) (newline)) lines) + (cons "" 0))] + ;; ,features — show what's compiled in + [(string=? expr-str "features") + (if (null? *jsh-enabled-features*) + (display "jsh: base core (no optional features)\n") + (begin + (display "jsh enabled features:\n") + (for-each + (lambda (entry) + (fprintf (current-output-port) " ~a ~a~n" (car entry) (cdr entry))) + *jsh-enabled-features*))) + (cons "" 0)] + ;; ,use file.ss + [(string-prefix? "use " expr-str) + (let ([result (handle-use-command + (substring expr-str 4 (string-length expr-str)))]) + (cons (format-result result) 0))] + ;; ,room — GC & heap report + [(or (string=? "room" expr-str) (string=? "room " expr-str)) + (room) + (cons "" 0)] + ;; ,room #t — verbose + [(string-prefix? "room " expr-str) + (room #t) + (cons "" 0)] + ;; ,out — capture stdout+stderr to file + [(or (string=? expr-str "out") + (string-prefix? "out " expr-str)) + (let ([rest (if (string=? expr-str "out") "" + (string-trim-whitespace + (substring expr-str 4 (string-length expr-str))))]) + (cond + ((or (string=? rest "") (string-prefix? "--help" rest) (string=? rest "-h")) + (display "Usage: ,out [-o file] <command>\n") + (display " Captures stdout and stderr from <command> at the fd level.\n") + (display " Output is displayed after the command completes and saved to a file.\n") + (display " -o file save output to <file> (default: /tmp/jsh-out-<pid>.txt)\n") + (display "Example: ,out -o build.log make -j8\n") + (display " ,out docker build .\n") + (cons "" 0)) + (else + (let* ([tokens (simple-tokenize rest)] + [out-file #f] + [cmd-tokens '()]) + ;; Parse -o file option + (let loop ([toks tokens]) + (cond + ((null? toks) (set! cmd-tokens '())) + ((and (string=? (car toks) "-o") (pair? (cdr toks))) + (set! out-file (cadr toks)) + (set! cmd-tokens (cddr toks))) + (else + (set! cmd-tokens toks)))) + (when (not out-file) + (set! out-file (string-append "/tmp/jsh-out-" + (number->string (ffi-getpid)) ".txt"))) + (if (null? cmd-tokens) + (begin + (display "jsh: ,out: no command specified\n") + (cons "" 1)) + (let* ([cmd-str (let loop ([toks cmd-tokens] [acc ""]) + (if (null? toks) acc + (loop (cdr toks) + (if (string=? acc "") + (car toks) + (string-append acc " " (car toks))))))] + [file-fd (ffi-open-raw out-file + (bitwise-ior O_WRONLY O_CREAT O_TRUNC) #o666)]) + (if (< file-fd 0) + (let ([reason + (cond + ((and (file-exists? out-file) + (file-directory? out-file)) + "Is a directory") + ((let ([dir (path-parent out-file)]) + (and (> (string-length dir) 0) + (not (file-exists? dir)))) + "No such file or directory") + (else "Permission denied"))]) + (fprintf (current-error-port) + "jsh: ,out: ~a: ~a~n" out-file reason) + (cons "" 1)) + (let* ([saved-out-port (current-output-port)] + [saved-err-port (current-error-port)] + [_ (begin (flush-output-port saved-out-port) + (flush-output-port saved-err-port))] + [saved-1 (ffi-dup 1)] + [saved-2 (ffi-dup 2)]) + (ffi-dup2 file-fd 1) + (ffi-dup2 file-fd 2) + (ffi-close-fd file-fd) + (let* ([cap-port (transcoded-port + (open-fd-output-port + (ffi-dup 1) (buffer-mode line)) + (make-transcoder (utf-8-codec)))] + [err-port (transcoded-port + (open-fd-output-port + (ffi-dup 2) (buffer-mode line)) + (make-transcoder (utf-8-codec)))]) + (current-output-port cap-port) + (current-error-port err-port) + (let ([status (guard (e [#t (fprintf err-port + "jsh: ~a~n" + (if (message-condition? e) + (condition-message e) + e)) + 1]) + (run-cmd cmd-str))]) + (flush-output-port cap-port) + (flush-output-port err-port) + (close-port cap-port) + (close-port err-port) + ;; Restore original fds + (ffi-dup2 saved-1 1) + (ffi-dup2 saved-2 2) + (ffi-close-fd saved-1) + (ffi-close-fd saved-2) + ;; Restore Scheme ports + (current-output-port saved-out-port) + (current-error-port saved-err-port) + ;; Display captured output + (guard (e [#t (void)]) + (let ([content (call-with-input-file out-file get-string-all)]) + (display content))) + (fprintf (current-error-port) + "~n[captured to ~a]~n" out-file) + (cons "" (if (integer? status) status 1))))))))))))] + ;; Normal Jerboa eval (catch-all) + [else + (if (meta-command-like? expr-str) + (unknown-meta-command expr-str) + (let* ([jerboa-forms (jerboa-read-all-from-string expr-str)] + [result (jerboa-eval-forms jerboa-forms)]) + (cons (format-result result) 0)))])))) + +;; ── Main entry ── + +(define (get-real-args) + (let ([argc-str (getenv "JSH_ARGC")]) + (if argc-str + (let ([argc (string->number argc-str)]) + (let loop ([i 0] [acc '()]) + (if (>= i argc) + (reverse acc) + (let ([val (getenv (format "JSH_ARG~a" i))]) + (loop (+ i 1) (cons (or val "") acc)))))) + (let ([cmdline (command-line)]) + (if (pair? cmdline) (cdr cmdline) '()))))) + +;; Inform main.ss whether this is an encrypted build +(*jsh-embed-encrypted* (embed-encrypted?)) + +;; Initialize prompt suffix +(update-prompt-suffix!) + +(dynamic-wind + (lambda () (void)) + (lambda () (apply main (get-real-args))) + (lambda () (void))) --- a/jsh-generate.ss +++ b/jsh-generate.ss @@ -4,7 +4,7 @@ ;;; Reads jsh.ss, replaces the line beginning with ;;; (define *jsh-enabled-features* ... ;;; with an expanded manifest of (name . description) pairs from features.def, -;;; and writes jsh-generated.ss. Always uses jsh.ss as the source — minimal +;;; and writes jsh-generated.ss. Always uses jsh.ss as the source — base ;;; vs. full builds differ only in the manifest contents (and which optional ;;; modules the platform build script chooses to compile). ;;; @@ -152,5 +152,5 @@ (close-output-port out) (printf " jsh-generated.ss: ~a (features: ~a)~n" - (if (null? enabled-features) "minimal" "full") + (if (null? enabled-features) "base" "full") (length enabled-features)))) deleted file mode 100644 --- a/jsh-minimal.ss +++ /dev/null @@ -1,446 +0,0 @@ -#!chezscheme -;; jsh-minimal.ss — Core-only jsh program (no optional features) -;; -;; This is the baseline program for builds with zero features enabled. -;; It provides: shell execution, ,help, ,features, ,room, ,out, ,use, ,<expr> -;; No coreutils, mux, ssh, vault, aws, record, sandbox, cage, rl, profiler, proxy. - -(import (chezscheme) (jsh main) (except (jsh builtins) list-head) - (jsh registry) (jsh script) - (only (jsh executor) ast->command-text) - (only (jsh environment) *command-trace-hook* env-exported-alist) - (only (jsh parser) parse-complete-command) - (only (jsh ast) ast-pipeline? ast-pipeline-commands ast-pipeline-bang? - simple-command? simple-command-assignments simple-command-words - assignment? assignment-name assignment-value assignment-op) - (only (jsh embed) embed-encrypted? embed-unlocked?) - (only (jsh ffi) ffi-termios-restore ffi-termios-save ffi-set-raw-mode - ffi-fork-exec ffi-nanosleep-us ffi-byte-ready? ffi-read-byte - ffi-fdwrite ffi-terminal-columns ffi-terminal-rows - ffi-signal-flag-install ffi-signal-flag-check - ffi-dup ffi-dup2 ffi-close-fd ffi-open-raw ffi-getpid - ffi-self-exe-path ffi-process-alive ffi-read-pidfile - ffi-fork ffi-exit ffi-setsid ffi-ensure-std-fds - O_WRONLY O_CREAT O_TRUNC) - (only (jsh util) expand-tilde) - (only (jsh history) history-add-raw! history-entries history-clear! - history-entry-timestamp history-entry-cwd history-entry-command - *history-save-override*) - (only (compiler compile) jerboa-compile-top) - (only (reader reader) jerboa-read) - (only (std os path) path-strip-directory path-directory) - ) - -;; ── Feature manifest (minimal = empty) ── -(define *jsh-enabled-features* '()) - -;; ── Session state ── - -(define *jsh-unlocked* #f) -(define *jsh-privacy-mode* #f) - -(define (update-prompt-suffix!) - (*jsh-prompt-suffix* - (let* ([e (embed-encrypted?)] - [u *jsh-unlocked*] - [lock-part (cond [(and e u) "🔑"] [e "🔒"] [else ""])]) - (if (string=? lock-part "") "" (string-append " " lock-part))))) - -(define (sync-history-enabled!) - (*jsh-history-enabled* (not *jsh-privacy-mode*))) - -;; ── Tier setup ── - -(*jsh-tier* "small") -;; Force (jsh builtins) registration side effects -(let () special-builtin? (void)) - -;; ── String helpers ── - -(define (output-to-string proc) - (let ([p (open-output-string)]) - (proc p) - (get-output-string p))) - -(define (string-trim-whitespace s) - (let* ([len (string-length s)] - [start (let lp ([i 0]) - (if (and (< i len) (char-whitespace? (string-ref s i))) - (lp (+ i 1)) i))] - [end (let lp ([i len]) - (if (and (> i start) (char-whitespace? (string-ref s (- i 1)))) - (lp (- i 1)) i))]) - (substring s start end))) - -(define (string-prefix? prefix str) - (and (>= (string-length str) (string-length prefix)) - (string=? (substring str 0 (string-length prefix)) prefix))) - -(define (simple-tokenize str) - (let ([n (string-length str)]) - (let lp ([i 0] [tokens '()]) - (cond - ((>= i n) (reverse tokens)) - ((char-whitespace? (string-ref str i)) - (lp (+ i 1) tokens)) - ((char=? (string-ref str i) #\") - (let qloop ([j (+ i 1)] [chars '()]) - (cond - ((>= j n) - (reverse (cons (list->string (reverse chars)) tokens))) - ((char=? (string-ref str j) #\") - (lp (+ j 1) (cons (list->string (reverse chars)) tokens))) - (else - (qloop (+ j 1) (cons (string-ref str j) chars)))))) - ((char=? (string-ref str i) #\') - (let qloop ([j (+ i 1)] [chars '()]) - (cond - ((>= j n) - (reverse (cons (list->string (reverse chars)) tokens))) - ((char=? (string-ref str j) #\') - (lp (+ j 1) (cons (list->string (reverse chars)) tokens))) - (else - (qloop (+ j 1) (cons (string-ref str j) chars)))))) - (else - (let wloop ([j i]) - (if (or (>= j n) (char-whitespace? (string-ref str j)) - (char=? (string-ref str j) #\") (char=? (string-ref str j) #\')) - (lp j (cons (substring str i j) tokens)) - (wloop (+ j 1))))))))) - -(define (simple-join lst sep) - (if (null? lst) "" - (let loop ([rest (cdr lst)] [acc (car lst)]) - (if (null? rest) acc - (loop (cdr rest) (string-append acc sep (car rest))))))) - -(define (meta-command-like? expr-str) - (let ([s (string-trim-whitespace expr-str)]) - (and (> (string-length s) 0) - (let ([ch (string-ref s 0)]) - (or (char-alphabetic? ch) - (char=? ch #\_)))))) -