Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: AC Native OS — Swank REPL, WiFi auto-connect, IP display

Add Swank server (port 4005) for remote Common Lisp REPL access.
Init script now auto-connects WiFi from saved USB credentials or
default hotspot, with IP address refresh for on-screen display.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+118 -2
+1 -1
fedac/native/Dockerfile.builder
··· 37 37 && sbcl --non-interactive \ 38 38 --load /tmp/ql.lisp \ 39 39 --eval '(quicklisp-quickstart:install :path "/opt/quicklisp/")' \ 40 - --eval '(ql:quickload (list :cffi :bordeaux-threads :alexandria))' \ 40 + --eval '(ql:quickload (list :cffi :bordeaux-threads :alexandria :swank))' \ 41 41 && rm /tmp/ql.lisp 42 42 43 43 # ── Pre-download QuickJS + Linux kernel source ──
+1 -1
fedac/native/cl/ac-native.asd
··· 6 6 :version "0.1.0" 7 7 :author "Aesthetic Computer" 8 8 :license "MIT" 9 - :depends-on ("cffi" "bordeaux-threads" "alexandria") 9 + :depends-on ("cffi" "bordeaux-threads" "alexandria" "swank") 10 10 :serial t 11 11 :components 12 12 ((:file "packages")
+52
fedac/native/cl/main.lisp
··· 130 130 (defvar *esc-count* 0) 131 131 (defvar *esc-last-frame* 0) 132 132 133 + ;;; ── Network info ── 134 + 135 + (defvar *ip-address* "" "Current IP address for Swank display.") 136 + 137 + (defun refresh-ip () 138 + "Read current IP address from system." 139 + (handler-case 140 + (let ((output (with-output-to-string (s) 141 + (sb-ext:run-program "/sbin/ip" '("-4" "-o" "addr" "show") 142 + :output s :error nil)))) 143 + (dolist (line (uiop:split-string output :separator '(#\Newline))) 144 + (when (and (search "inet " line) 145 + (not (search "127.0.0.1" line))) 146 + ;; Extract IP from "X: wlan0 inet 192.168.1.x/24 ..." 147 + (let* ((inet-pos (search "inet " line)) 148 + (ip-start (+ inet-pos 5)) 149 + (slash-pos (position #\/ line :start ip-start))) 150 + (when slash-pos 151 + (setf *ip-address* (subseq line ip-start slash-pos)) 152 + (return)))))) 153 + (error () nil))) 154 + 133 155 ;;; ── Main ── 134 156 135 157 (defun main () ··· 174 196 175 197 ;; Font init 176 198 (font-init) 199 + 200 + ;; Start Swank server for remote REPL (port 4005) 201 + (handler-case 202 + (progn 203 + (setf swank::*communication-style* :spawn) 204 + (swank:create-server :port 4005 :dont-close t) 205 + (format *error-output* "[notepat] Swank server on port 4005~%") 206 + ;; Log IP address for connection 207 + (handler-case 208 + (let ((output (with-output-to-string (s) 209 + (sb-ext:run-program "/sbin/ip" '("-4" "addr" "show") 210 + :output s :error nil)))) 211 + (dolist (line (uiop:split-string output :separator '(#\Newline))) 212 + (when (search "inet " line) 213 + (format *error-output* "[notepat] ~A~%" (string-trim '(#\Space) line))))) 214 + (error () nil)) 215 + (force-output *error-output*)) 216 + (error (e) 217 + (format *error-output* "[notepat] Swank failed: ~A~%" e) 218 + (force-output *error-output*))) 177 219 178 220 ;; Main loop 179 221 (setf *running* t) ··· 370 412 (let ((txt (format nil "~D" vc))) 371 413 (graph-ink graph (make-color :r 200 :g 200 :b 200 :a 180)) 372 414 (font-draw graph txt (- sw (* (length txt) 6) 3) 3)))) 415 + 416 + ;; IP + Swank indicator (top, centered) 417 + (when (> (length *ip-address*) 0) 418 + (let ((txt (format nil "~A:4005" *ip-address*))) 419 + (graph-ink graph (make-color :r 60 :g 180 :b 60 :a 160)) 420 + (font-draw graph txt (- (floor sw 2) (floor (font-measure txt) 2)) 3))) 421 + 422 + ;; Refresh IP every ~5 seconds (300 frames) 423 + (when (zerop (mod frame 300)) 424 + (refresh-ip)) 373 425 374 426 ;; ── Present ── 375 427 (ac-native.drm:drm-present display screen scale)
+64
fedac/native/initramfs/init
··· 49 49 export SSL_CERT_FILE="/etc/pki/tls/certs/ca-bundle.crt" 50 50 export CURL_CA_BUNDLE="/etc/pki/tls/certs/ca-bundle.crt" 51 51 export SSL_CERT_DIR="/etc/ssl/certs" 52 + export HOME="/tmp" 53 + 54 + # ── WiFi auto-connect ── 55 + # Mount USB EFI partition to read saved credentials 56 + USB_PART="" 57 + for p in /dev/sda1 /dev/sdb1 /dev/nvme0n1p1; do 58 + if [ -b "$p" ]; then 59 + mkdir -p /mnt 60 + mount -t vfat "$p" /mnt 2>/dev/null && { 61 + if [ -f /mnt/EFI/BOOT/BOOTX64.EFI ]; then USB_PART="$p"; break; fi 62 + umount /mnt 2>/dev/null 63 + } 64 + fi 65 + done 66 + 67 + # Find wireless interface 68 + WLAN="" 69 + for d in /sys/class/net/wlan* /sys/class/net/wlp*; do 70 + [ -d "$d" ] && WLAN=$(basename "$d") && break 71 + done 72 + 73 + if [ -n "$WLAN" ]; then 74 + ip link set "$WLAN" up 2>/dev/null 75 + mkdir -p /var/run/wpa_supplicant /etc/wpa_supplicant 76 + 77 + # Build wpa_supplicant config from saved creds or default hotspot 78 + WPA_CONF="/etc/wpa_supplicant/wpa_supplicant.conf" 79 + cat > "$WPA_CONF" <<WPAEOF 80 + ctrl_interface=/var/run/wpa_supplicant 81 + update_config=1 82 + WPAEOF 83 + 84 + # Read saved WiFi credentials from USB 85 + if [ -f /mnt/wifi_creds.json ] && command -v busybox >/dev/null; then 86 + # Simple JSON array parse: extract ssid/pass pairs 87 + # Format: [{"ssid":"name","pass":"pw"}, ...] 88 + cat /mnt/wifi_creds.json | sed 's/},{/}\n{/g' | while read -r line; do 89 + SSID=$(echo "$line" | sed -n 's/.*"ssid"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') 90 + PASS=$(echo "$line" | sed -n 's/.*"pass"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') 91 + if [ -n "$SSID" ] && [ -n "$PASS" ]; then 92 + cat >> "$WPA_CONF" <<NETEOF 93 + network={ 94 + ssid="$SSID" 95 + psk="$PASS" 96 + } 97 + NETEOF 98 + fi 99 + done 100 + fi 101 + 102 + # Always add default hotspot 103 + cat >> "$WPA_CONF" <<DEFEOF 104 + network={ 105 + ssid="aesthetic.computer" 106 + psk="aesthetic.computer" 107 + } 108 + DEFEOF 109 + 110 + # Start wpa_supplicant in background 111 + wpa_supplicant -B -i "$WLAN" -c "$WPA_CONF" 2>/dev/null 112 + 113 + # Start dhclient in background (will get IP when WiFi connects) 114 + (sleep 5 && dhclient -nw "$WLAN" 2>/dev/null) & 115 + fi 52 116 53 117 # Run ac-native in a loop — if it crashes, restart; if clean exit, shutdown 54 118 CRASH_COUNT=0