mlx: break Qwen repetition loop; add /expert; jerbuild migration WIP
ober
5bd60cfd52483045b31418f4c16961d98738fd06
new file mode 100644 --- /dev/null +++ b/.jerbuild @@ -0,0 +1,37 @@ +;; .jerbuild — build a standalone jcode binary with `jerbuild build`. +;; +;; jerbuild bundles Chez Scheme + the jerboa stdlib, so NO jerboa source +;; checkout or external Chez is required. The only external input is the native +;; Rust lib (libjerboa_native), built from vendor/jerboa-native-rs with just the +;; features jcode needs (tls + crypto) — see the `native-rs` Makefile target. +;; Platform link flags (frameworks, -lsqlite3, -lc++, ...) are passed by the +;; Makefile via `jerbuild build --os-libs "..."` (per-uname). +;; +;; Static FFI needs NO source patching: chez-sqlite.sls falls through to (void) +;; and tui-ffi.ss honors JERBOA_STATIC=1 (set by support/jcode-main.c), so the +;; linked-in shim symbols resolve via the registered FFI symbols below. + +(entry "main-binary.ss") +(output "jcode") + +;; do-binary-build auto-appends the bundled stdlib; list only project + vendor. +(libdirs "lib" + "vendor/chez-sqlite/src" + "vendor/jerboa-websearch/src") + +;; Symbols statically registered via Sforeign_symbol (sqlite shim, termbox TUI +;; shim, jerboa-native tls+crypto). The custom main.c #includes + calls them. +(ffi-symbols "support/ffi-symbols.list") + +;; Stock jerbuild main.c + setenv(JERBOA_STATIC=1); see the file's header. +(main-c "support/jcode-main.c") + +;; C shims compiled into the binary. sqlite3.h is provided by the macOS SDK / +;; system; add an -I here if a host needs an explicit path. +(extra-sources + "vendor/chez-sqlite/chez_sqlite_shim.c" + ("src/jcode/ui/jcode_tui_shim.c" cflags: "-DTB_OPT_ATTR_W=32 -Ivendor/termbox2")) + +;; jerboa-native (Rust): tls + crypto only (no duckdb/pcap/postgres). jerbuild +;; runs cargo and links the resulting libjerboa_native.a. +(rust-crates ("vendor/jerboa-native-rs/Cargo.toml" features: "tls,crypto")) --- a/Makefile +++ b/Makefile @@ -1,35 +1,69 @@ +# jerbuild is the self-contained build tool: it bundles Chez Scheme + the +# jerboa stdlib under ~/.cache/jerbuild/<sha>/, so the host dev loop and the +# native `binary` need NO jerboa source checkout and NO separately-built Chez. +JERBUILD ?= jerbuild + +# Bundled stdlib path (Chez + jerboa libs live under $(JH)/lib). Resolved by +# asking the jerbuild binary where it unpacked its bundle. +JH := $(shell $(JERBUILD) --jerboa-home 2>/dev/null) +ifeq ($(JH),) +$(error jerbuild not found on PATH (or '$(JERBUILD) --jerboa-home' failed). Install jerbuild, or set JERBUILD=/path/to/jerbuild) +endif + +# JERBOA_HOME / SCHEME are used ONLY by the cross-compile targets +# (linux*/freebsd*/android) and the legacy `binary-legacy` fallback — the host +# dev loop and `make binary` no longer touch them. JERBOA_HOME ?= $(HOME)/mine/jerboa -SCHEME ?= $(JERBOA_HOME)/.chez/bin/scheme -LIBDIRS = --libdirs $(JERBOA_HOME)/lib:./lib:vendor/chez-sqlite/src:vendor/jerboa-websearch/src -JERBUILD = $(SCHEME) --libdirs $(JERBOA_HOME)/lib --script $(JERBOA_HOME)/jerbuild.ss +SCHEME ?= $(JERBOA_HOME)/.chez/bin/scheme FREEBSD_AMD64_CC ?= $(JERBOA_HOME)/support/cross-cc-freebsd-amd64 -# Fail early with a clear pointer if the jerboa-local Chez build is missing. -ifeq ($(wildcard $(SCHEME)),) -$(error Chez Scheme not found at $(SCHEME). Run 'make chez' in $(JERBOA_HOME) (or set SCHEME=... to override).) -endif +LIBDIRS := --libdirs ./lib:vendor/chez-sqlite/src:vendor/jerboa-websearch/src:$(JH)/lib +JEXEC := $(JERBUILD) exec $(LIBDIRS) -# Library paths for FFI shared objects (macOS: dylib, Linux: so) +# Library paths for FFI shared objects (macOS: dylib, Linux: so). The native +# Rust lib now lives in ./lib (dropped by the native-rs target), matching the +# (std crypto native-rust) loader's CWD-relative "lib/" fallback. SQLITE_LIB_DIR := $(shell brew --prefix sqlite 2>/dev/null)/lib SHIM_DIR := $(CURDIR)/vendor/chez-sqlite TUI_SHIM_DIR := $(CURDIR)/vendor/termbox2 -NATIVE_LIB_DIR := $(JERBOA_HOME)/lib +NATIVE_LIB_DIR := $(CURDIR)/lib LDPATH := $(SHIM_DIR):$(TUI_SHIM_DIR):$(SQLITE_LIB_DIR):$(NATIVE_LIB_DIR) -.PHONY: all help build gen run test test-providers clean repl binary install tui-shim run-tui jcode-musl linux linux-amd64 linux-arm64 jcode-linux-amd64 linux-docker linux-check linux-local docker test-linux test-linux-amd64 purge-stale sqlite-shim android android-clean vendor-deps vendor-clean +# Native Rust lib (vendored crate, built with cargo). Only the features jcode +# uses — tls + crypto; no duckdb/pcap/postgres. +NATIVE_DIR := vendor/jerboa-native-rs +NATIVE_FEATURES ?= tls,crypto +NATIVE_A := $(NATIVE_DIR)/target/release/libjerboa_native.a +NATIVE_DYLIB := $(NATIVE_DIR)/target/release/libjerboa_native.dylib +NATIVE_SO := $(NATIVE_DIR)/target/release/libjerboa_native.so + +# Per-OS link flags handed to `jerbuild build --os-libs`. jerbuild appends +# these after the Chez kernel + our archives when linking the binary. +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Darwin) +JCODE_OS_LIBS := -lm -lpthread -lncurses -liconv -lc++ -L$(SQLITE_LIB_DIR) -lsqlite3 -framework Security -framework CoreFoundation +else ifeq ($(UNAME_S),FreeBSD) +JCODE_OS_LIBS := -lm -lpthread -lncurses -L/usr/local/lib -liconv -lsqlite3 +else +JCODE_OS_LIBS := -lm -ldl -lpthread -luuid -lncurses -lsqlite3 -lstdc++ +endif + +.PHONY: all help build gen run test test-providers clean repl binary binary-legacy install tui-shim run-tui native-rs jcode-musl linux linux-amd64 linux-arm64 jcode-linux-amd64 linux-docker linux-check linux-local docker test-linux test-linux-amd64 purge-stale sqlite-shim android android-clean vendor-deps vendor-clean all: help help: @echo "Available targets:" - @echo " build Compile src/ → lib/ and pre-compile imports" + @echo " build Compile src/ → lib/ and pre-compile imports (jerbuild)" @echo " test Run test suite (test/run.ss)" @echo " test-providers Live 'say hello' smoke test per configured provider" @echo " run Start interactive agent REPL" @echo " run-tui Start TUI mode" @echo " repl Open a bare Scheme REPL with project libdirs" - @echo " gen Run jerbuild + rebuild sqlite/TUI shims" - @echo " binary Build native binary (macOS)" + @echo " gen Transpile src/ → lib/ + rebuild sqlite shim (jerbuild)" + @echo " native-rs Build vendored libjerboa_native (cargo, tls+crypto)" + @echo " binary Build native binary via 'jerbuild build' (.jerbuild)" + @echo " binary-legacy Fallback binary via build-binary.ss (needs jerboa checkout)" @echo " install Install binary to ~/.local/bin" @echo " linux Build Linux x86_64 static binary (cross-compile from this host)" @echo " linux-amd64 Cross-compile static jcode-linux-amd64 (default)" @@ -86,31 +120,34 @@ sqlite-shim: vendor/chez-sqlite vendor/chez-sqlite/chez_sqlite_shim.c -lsqlite3; \ fi -# ── libjerboa_native.so (Rust FFI) ────────────────────────────────────────── +# ── libjerboa_native (Rust FFI) ───────────────────────────────────────────── # secrets.ss + std/net/request + std/regex etc. all bind FFI symbols from -# libjerboa_native at library load time, so the .so must exist in -# $(NATIVE_LIB_DIR) (== $(JERBOA_HOME)/lib) before scheme starts. Without -# this, `make build` dies with "no entry for jerboa_last_error" the moment -# any library importing (std crypto native-rust) is visited. +# libjerboa_native at library load time, so the shared lib must exist in +# ./lib before scheme starts. Without it, `make build` dies with +# "no entry for jerboa_last_error" the moment any library importing +# (std crypto native-rust) is visited. # -# Defer to jerboa's own `make native` so we don't need to know about the -# cargo target dir or how the .so gets ad-hoc-signed on macOS. -NATIVE_SO := $(NATIVE_LIB_DIR)/libjerboa_native.so -NATIVE_DYLIB := $(NATIVE_LIB_DIR)/libjerboa_native.dylib -native-so: - @if [ ! -f "$(NATIVE_SO)" ] && [ ! -f "$(NATIVE_DYLIB)" ]; then \ - echo "=== libjerboa_native missing — building via $(JERBOA_HOME) ==="; \ - $(MAKE) -C $(JERBOA_HOME) native; \ +# We vendor the crate (sparse-clone from the jerboa monorepo) and build it +# with cargo using ONLY the features jcode needs (tls + crypto) — no +# dependency on a ~/mine/jerboa checkout. The same crate + features feed the +# static `binary` build via (rust-crates ...) in .jerbuild. +native-rs: $(NATIVE_DIR) + @command -v cargo >/dev/null 2>&1 || { \ + echo "ERROR: cargo not found on PATH. Install rustup from rustup.rs"; exit 1; } + @if [ ! -f "$(NATIVE_A)" ] || \ + [ -n "$$(find $(NATIVE_DIR)/src -name '*.rs' -newer $(NATIVE_A) 2>/dev/null)" ]; then \ + echo "=== Building libjerboa_native (features: $(NATIVE_FEATURES)) ==="; \ + ( cd $(NATIVE_DIR) && cargo build --release --features $(NATIVE_FEATURES) ); \ fi - @# On macOS, `make native` produces only .dylib but the jerboa stdlib - @# loaders (std crypto native-rust, std regex-native, std native) only - @# try the .so extension. Symlink so visit-time load-shared-object - @# succeeds. Without this, freshly recompiling those libraries (e.g. - @# after `make purge-stale` or a cross-build) dies with - @# "no entry for jerboa_last_error". - @if [ ! -f "$(NATIVE_SO)" ] && [ -f "$(NATIVE_DYLIB)" ]; then \ - echo "=== symlinking libjerboa_native.so -> libjerboa_native.dylib ==="; \ - ln -sf libjerboa_native.dylib "$(NATIVE_SO)"; \ + @mkdir -p lib + @# The dev loop loads a shared object; the binary links the .a (jerbuild + @# builds that itself via rust-crates). Drop the dylib/so into ./lib and, + @# on macOS, symlink the .so name the loader tries first. + @if [ -f "$(NATIVE_DYLIB)" ]; then \ + cp -f "$(NATIVE_DYLIB)" lib/libjerboa_native.dylib; \ + ln -sf libjerboa_native.dylib lib/libjerboa_native.so; \ + elif [ -f "$(NATIVE_SO)" ]; then \ + cp -f "$(NATIVE_SO)" lib/libjerboa_native.so; \ fi # ─── Vendor Dependencies ───────────────────────────────────────────────────── @@ -136,19 +173,30 @@ vendor/jerboa-websearch: @echo "=== Cloning jerboa-websearch into vendor/ ===" @git clone --depth 1 git@git.sr.ht:~lisp/jerboa-websearch vendor/jerboa-websearch +# Sparse-clone just the jerboa-native-rs crate out of the jerboa monorepo. +# Depended on only by native-rs (not vendor-deps), so cross targets — which +# build their own target-triple native lib — don't pull it. +$(NATIVE_DIR): + @mkdir -p vendor + @echo "=== Sparse-cloning jerboa-native-rs from the jerboa monorepo ===" + @git clone --depth 1 --filter=blob:none --sparse git@git.sr.ht:~lisp/jerboa vendor/.jerboa-monorepo + @git -C vendor/.jerboa-monorepo sparse-checkout set jerboa-native-rs + @mv vendor/.jerboa-monorepo/jerboa-native-rs $(NATIVE_DIR) + @rm -rf vendor/.jerboa-monorepo + vendor-clean: rm -rf vendor -gen: vendor-deps purge-stale sqlite-shim native-so - $(JERBUILD) src lib +gen: vendor-deps purge-stale sqlite-shim + $(JERBUILD) transpile src lib -build: gen +build: gen native-rs DYLD_LIBRARY_PATH=$(LDPATH) LD_LIBRARY_PATH=$(LDPATH) \ - $(SCHEME) $(LIBDIRS) --compile-imported-libraries --script main.ss < /dev/null + $(JERBUILD) compile $(LIBDIRS) main.ss < /dev/null -run: +run: native-rs DYLD_LIBRARY_PATH=$(LDPATH) LD_LIBRARY_PATH=$(LDPATH) \ - $(SCHEME) $(LIBDIRS) --script main.ss + $(JEXEC) main.ss tui-shim: vendor/termbox2 @test -f $(TUI_SHIM_DIR)/jcode_tui_shim.dylib \ @@ -158,27 +206,40 @@ tui-shim: vendor/termbox2 -o $(TUI_SHIM_DIR)/jcode_tui_shim.dylib \ src/jcode/ui/jcode_tui_shim.c -run-tui: build tui-shim +run-tui: build tui-shim native-rs DYLD_LIBRARY_PATH=$(LDPATH) LD_LIBRARY_PATH=$(LDPATH) \ - $(SCHEME) $(LIBDIRS) --script main.ss --tui + $(JEXEC) main.ss --tui repl: - $(SCHEME) $(LIBDIRS) + DYLD_LIBRARY_PATH=$(LDPATH) LD_LIBRARY_PATH=$(LDPATH) \ + $(JEXEC) support/repl.ss test: build DYLD_LIBRARY_PATH=$(LDPATH) LD_LIBRARY_PATH=$(LDPATH) \ - $(SCHEME) $(LIBDIRS) --script test/run.ss + $(JEXEC) test/run.ss # Live smoke test — one round-trip per provider with a configured key. # Providers without a key are SKIPped, not failed. Excluded from `test` # because it makes real network calls. test-providers: build DYLD_LIBRARY_PATH=$(LDPATH) LD_LIBRARY_PATH=$(LDPATH) \ - $(SCHEME) $(LIBDIRS) --script test/run-providers.ss + $(JEXEC) test/run-providers.ss # --- Static binary targets --- -binary: build +# Self-contained: `jerbuild build` reads .jerbuild (entry, libdirs, ffi-symbols, +# main-c, C shims, rust-crate), bundles Chez + stdlib, runs cargo for the native +# .a, and links it all. Per-OS link flags come from --os-libs. +binary: gen native-rs + DYLD_LIBRARY_PATH=$(LDPATH) LD_LIBRARY_PATH=$(LDPATH) \ + $(JERBUILD) build --os-libs "$(JCODE_OS_LIBS)" + cp vendor/termbox2/jcode_tui_shim.dylib ./jcode_tui_shim.dylib 2>/dev/null || true + cp vendor/termbox2/jcode_tui_shim.so ./jcode_tui_shim.so 2>/dev/null || true + +# Fallback: the hand-rolled WPO build. Requires a full ~/mine/jerboa checkout +# with a locally-built Chez ($(SCHEME)) and `make -C $(JERBOA_HOME) native` +# already run. Kept until `binary` is proven across hosts, then removed. +binary-legacy: gen tui-shim JERBOA_HOME=$(JERBOA_HOME) \ DYLD_LIBRARY_PATH=$(LDPATH) LD_LIBRARY_PATH=$(LDPATH) \ $(SCHEME) -q --libdirs $(JERBOA_HOME)/lib:./lib:vendor/chez-sqlite/src:vendor/jerboa-websearch/src \ @@ -213,7 +274,7 @@ linux-docker: linux-check docker linux-check: gen @echo "=== Running linux-check (fast musl-build drift check) ===" DYLD_LIBRARY_PATH=$(LDPATH) LD_LIBRARY_PATH=$(LDPATH) \ - $(SCHEME) -q $(LIBDIRS) --script linux-check.ss + $(SCHEME) -q --libdirs "$(XC_LIBDIRS)" --script linux-check.ss # ── Docker build (canonical static binary, zero runtime deps) ─────────────── # Use `make linux` to build in Docker (canonical, reproducible). @@ -356,6 +417,7 @@ clean: find . -name "*.so" -delete find . -name "*.wpo" -delete rm -f jcode jcode-musl jcode-musl.sha256 + rm -f lib/libjerboa_native.dylib lib/libjerboa_native.so jcode_tui_shim.dylib jcode_tui_shim.so # ── Android APK (thin client, no embedded binary) ─────────────────────────── # Build from Termux — no Android Studio / Gradle required. --- a/src/jcode/provider/sampling.ss +++ b/src/jcode/provider/sampling.ss @@ -188,14 +188,22 @@ (else (get-sampling-defaults model))))) ;; Splat the resolved sampling params onto an OpenAI-style request body. When -;; no row applies (unknown model, or policy 'off), preserve the legacy mlx -;; safety net: local mlx-lm LoRAs regress to a degenerate fixed point at -;; temperature=0 with no repetition penalty, so nudge them off it. +;; no row applies (unknown model, or policy 'off), apply the mlx safety net. +;; The mlx model id is a filesystem path (.../Qwen3.6-35B-A3B-TurboQuant-6bit), +;; so it never matches a card-identity row — every local mlx model lands here. +;; Nearly all are Qwen3-family, and every Qwen3 card warns that near-greedy +;; decoding "leads to endless repetitions". The old temp=0.3 / rep=1.05 net was +;; effectively greedy and the 35B-A3B 6-bit quant degenerated into a token loop; +;; temp=0.6 + top_p=0.95 + top_k=20 + min_p=0 is Qwen's recommended agentic +;; preset and repetition_penalty=1.1 is heavy-quant insurance (all keys honored +;; by mlx-lm's server sampler). (def (apply-sampling-to-body! body model provider-name) (let ((params (sampling-params-for model))) (if (pair? (hash-keys params)) (hash-for-each (lambda (k v) (hash-put! body k v)) params) (when (equal? provider-name "mlx") - (hash-put! body "temperature" 0.3) - (hash-put! body "top_p" 0.9) - (hash-put! body "repetition_penalty" 1.05))))) + (hash-put! body "temperature" 0.6) + (hash-put! body "top_p" 0.95) + (hash-put! body "top_k" 20) + (hash-put! body "min_p" 0.0) + (hash-put! body "repetition_penalty" 1.1))))) --- a/src/jcode/ui/cli.ss +++ b/src/jcode/ui/cli.ss @@ -722,7 +722,7 @@ EXAMPLES: (let ((cmd (string-trim (substring input 1 (string-length input))))) (cond ((equal? cmd "help") - (display "\nCommands:\n /help Show this help\n /model [name] Show or set model\n /provider [name] Show or set provider\n /plan Switch to PLAN mode (read-only)\n /build Switch to BUILD mode (read+write)\n /mode Show current mode\n /mcp Toggle MCP tools on/off\n /tools List available tools\n /clear Start a new session\n /sessions List saved sessions\n /compact Show message count\n /undo [N] Revert last N checkpoint(s) (default 1)\n /checkpoints List recent shadow-git checkpoints\n /forge [on|off] Show or toggle forge guardrails\n /forge sampling <off|on|strict> Per-model sampling policy\n /forge workflow Describe + self-test the workflow engine\n /forge proxy Describe + self-test the OpenAI-compatible proxy\n /forge ablation Describe + self-test the eval/ablation harness\n /forge verify Describe + self-test the verify-gate (ATLAS verify+repair)\n /forge bestofk Describe + self-test best-of-k diverse-gen (ATLAS Phase-1)\n /forge breaker Describe + self-test the no-progress loop breaker\n /forge run <task> Verify-gated coding on your live model (edit→verify→done)\n /quit Exit\n\nMulti-line: end a line with \\ to continue on the next line.\n\n")) + (display "\nCommands:\n /help Show this help\n /model [name] Show or set model\n /provider [name] Show or set provider\n /expert <prompt> Route one prompt to the configured expert model\n /plan Switch to PLAN mode (read-only)\n /build Switch to BUILD mode (read+write)\n /mode Show current mode\n /mcp Toggle MCP tools on/off\n /tools List available tools\n /clear Start a new session\n /sessions List saved sessions\n /compact Show message count\n /undo [N] Revert last N checkpoint(s) (default 1)\n /checkpoints List recent shadow-git checkpoints\n /forge [on|off] Show or toggle forge guardrails\n /forge sampling <off|on|strict> Per-model sampling policy\n /forge workflow Describe + self-test the workflow engine\n /forge proxy Describe + self-test the OpenAI-compatible proxy\n /forge ablation Describe + self-test the eval/ablation harness\n /forge verify Describe + self-test the verify-gate (ATLAS verify+repair)\n /forge bestofk Describe + self-test best-of-k diverse-gen (ATLAS Phase-1)\n /forge breaker Describe + self-test the no-progress loop breaker\n /forge run <task> Verify-gated coding on your live model (edit→verify→done)\n /quit Exit\n\nMulti-line: end a line with \\ to continue on the next line.\n\n")) ((equal? cmd "model") (printf "Provider: ~a~n" (or (current-provider-override) (config-provider))) (printf "Model: ~a~n" (or (current-model-override) (config-model))) @@ -738,6 +738,24 @@ EXAMPLES: (current-provider-override new-provider) (current-model-override (config-default-model new-provider)) (printf "Provider set to: ~a (model: ~a)~n" new-provider (current-model-override)))) + ((or (equal? cmd "expert") (string-prefix? "expert " cmd)) + ;; Route one prompt to the configured expert. No thread boundary here + ;; (line-mode runs the turn synchronously), so parameterize wraps the + ;; agent call directly and the overrides revert when the turn returns. + (let ((prompt (if (equal? cmd "expert") "" + (string-trim (substring cmd 7 (string-length cmd))))) + (ep (config-ref "expert" "provider")) + (em (config-ref "expert" "model"))) + (cond + ((not (and ep em)) + (printf "No expert configured. Add an \"expert\" block to jcode.json.~n")) + ((string=? prompt "") + (printf "Usage: /expert <prompt> — routes one prompt to ~a/~a~n" ep em)) + (else + (printf "Routing to expert: ~a/~a~n" ep em) + (parameterize ((current-provider-override ep) + (current-model-override em)) + (handle-user-input prompt session-id)))))) ((equal? cmd "tools") (printf "Available tools: ~a~n" (string-join (list-tools) ", "))) ((equal? cmd "mcp") --- a/src/jcode/ui/tui-input.ss +++ b/src/jcode/ui/tui-input.ss @@ -43,6 +43,7 @@ '(("/help" . "Show help") ("/model" . "Show or set model") ("/provider" . "Show or set provider") + ("/expert" . "Force a prompt to the configured expert model") ("/tools" . "List available tools") ("/clear" . "Start new session") ("/sessions" . "List saved sessions") new file mode 100644 --- /dev/null +++ b/support/ffi-symbols.list @@ -0,0 +1,98 @@ +# FFI symbols statically registered via Sforeign_symbol() in the jcode binary. +# One symbol per line; blank lines and # comments ignored. +# Consumed by `.jerbuild` (ffi-symbols) -> jerbuild emits ffi_symbols.h. +# +# These resolve the foreign-procedure calls made by the linked-in C shims +# (chez_sqlite, termbox TUI) and the jerboa-native Rust lib (tls + crypto). +# All are present in libjerboa_native.a built with --features tls,crypto and +# in the two C shims. + +# ── chez-sqlite shim ───────────────────────────────────────────── +chez_sqlite_open +chez_sqlite_close +chez_sqlite_exec +chez_sqlite_prepare +chez_sqlite_finalize +chez_sqlite_reset +chez_sqlite_clear_bindings +chez_sqlite_step +chez_sqlite_column_count +chez_sqlite_column_name +chez_sqlite_column_type +chez_sqlite_column_int64 +chez_sqlite_column_double +chez_sqlite_column_text +chez_sqlite_column_bytes +chez_sqlite_column_blob +chez_sqlite_bind_int64 +chez_sqlite_bind_double +chez_sqlite_bind_text +chez_sqlite_bind_blob +chez_sqlite_bind_null +chez_sqlite_last_insert_rowid +chez_sqlite_changes +chez_sqlite_errmsg +chez_SQLITE_ROW +chez_SQLITE_DONE +chez_SQLITE_OK + +# ── termbox2 TUI shim ──────────────────────────────────────────── +jcode_tb_init +jcode_tb_shutdown +jcode_tb_width +jcode_tb_height +jcode_tb_clear +jcode_tb_present +jcode_tb_set_cursor +jcode_tb_hide_cursor +jcode_tb_change_cell +jcode_tb_set_clear_attrs +jcode_tb_print +jcode_tb_printf +jcode_tb_set_input_mode +jcode_tb_set_output_mode +jcode_tb_poll_event +jcode_tb_peek_event +jcode_tb_event_type +jcode_tb_event_mod +jcode_tb_event_key +jcode_tb_event_ch +jcode_tb_event_w +jcode_tb_event_h +jcode_tb_event_x +jcode_tb_event_y + +# ── jerboa-native (TLS / rustls) ───────────────────────────────── +jerboa_tls_server_new +jerboa_tls_server_new_mtls +jerboa_tls_server_free +jerboa_tls_accept +jerboa_tls_connect +jerboa_tls_connect_pinned +jerboa_tls_connect_mtls +jerboa_tls_close +jerboa_tls_read +jerboa_tls_write +jerboa_tls_flush +jerboa_tls_get_fd +jerboa_tls_set_nonblock +jerboa_last_error + +# ── jerboa-native (crypto / ring) ──────────────────────────────── +jerboa_sha1 +jerboa_sha256 +jerboa_sha384 +jerboa_sha512 +jerboa_random_bytes +jerboa_hmac_sha256 +jerboa_hmac_sha256_verify +jerboa_timing_safe_equal +jerboa_aead_seal +jerboa_aead_open +jerboa_chacha20_seal +jerboa_chacha20_open +jerboa_scrypt +jerboa_pbkdf2_derive +jerboa_pbkdf2_verify +jerboa_argon2id_hash +jerboa_argon2id_verify new file mode 100644 --- /dev/null +++ b/support/jcode-main.c @@ -0,0 +1,54 @@ +/* Custom main.c for the standalone jcode binary (referenced by .jerbuild). + * + * Identical to jerbuild's stock template, with ONE addition: it sets + * JERBOA_STATIC=1 before building the heap. (jcode ui tui-ffi) reads that env + * var at library-load time and, when set, trusts the termbox shim symbols that + * are linked into the binary (and registered via register_ffi_symbols below) + * instead of trying to dlopen a jcode_tui_shim.{so,dylib} that does not exist + * in a static build. chez-sqlite needs no such flag — it already falls through + * to (void) and resolves its symbols the same way. + */ +#include "scheme.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> + +#include "petite_boot.h" +#include "scheme_boot.h" +#include "program_boot.h" +#include "ffi_symbols.h" + +static const char *write_program_tmpfile(void) { + static char path[] = "/tmp/jcode-prog-XXXXXX"; + int fd = mkstemp(path); + if (fd < 0) { perror("mkstemp"); exit(1); } + ssize_t n = write(fd, program_boot_data, program_boot_size); + if (n != (ssize_t)program_boot_size) { + perror("write"); close(fd); unlink(path); exit(1); + } + close(fd); + return path; +} + +int main(int argc, const char *argv[]) { + /* Trust the linked-in termbox shim symbols (see file header). */ + setenv("JERBOA_STATIC", "1", 1); + + Sscheme_init(NULL); + Sregister_boot_file_bytes("petite", + (void *)petite_boot_data, petite_boot_size); + Sregister_boot_file_bytes("scheme", + (void *)scheme_boot_data, scheme_boot_size); + Sbuild_heap(NULL, NULL); + register_ffi_symbols(); + + const char *prog_path = write_program_tmpfile(); + int status = Sscheme_program(prog_path, argc, argv); + unlink(prog_path); + + Sscheme_deinit(); + return status; +} new file mode 100644 --- /dev/null +++ b/support/repl.ss @@ -0,0 +1,6 @@ +#!chezscheme +;; Bare REPL with the project libdirs loaded, for `make repl` under jerbuild +;; (jerbuild exec needs a script; there is no bare-REPL subcommand). +(import (chezscheme)) +(printf "jcode REPL (jerbuild) — project libdirs loaded. (exit) to quit.~n") +(new-cafe)