From 2f1074f7019000160f80a6c03dc705acbfb89d55 Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Wed, 23 Sep 2026 13:11:35 -0700 Subject: [PATCH] native: flash-mac.sh mounts FAT through diskutil on macOS 26, honors an inscribed boot piece and monoOutput; flash-usbs.sh macOS 26 dropped the msdosfs kext, so mount_msdos fails; diskutil mount reaches the userland driver and still works on older releases. The boot piece now also comes from usbConfig.piece in the inscription, and mono can be pinned to left, right or both. flash-usbs.sh flashes a batch of sticks. --- fedac/native/scripts/flash-mac.sh | 24 +++++++++++++----- fedac/native/scripts/flash-usbs.sh | 40 ++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 6 deletions(-) create mode 100755 fedac/native/scripts/flash-usbs.sh diff --git a/fedac/native/scripts/flash-mac.sh b/fedac/native/scripts/flash-mac.sh index 737fc8a088..2d31ece438 100755 --- a/fedac/native/scripts/flash-mac.sh +++ b/fedac/native/scripts/flash-mac.sh @@ -453,6 +453,16 @@ mount_fat() { # $1=partition device $2=mount point mount_msdos "$1" "$2" } log "Mounting partitions…" +# macOS 26+ no longer loads the msdosfs kext (mount_msdos fails with "msdos +# filesystem is not available"); FAT is mounted by a userland driver that +# diskutil knows how to reach. diskutil mount also works on older releases. +mount_fat() { + local part="$1" mnt="$2" + diskutil unmount "${part}" >/dev/null 2>&1 || true + diskutil mount -mountPoint "${mnt}" "${part}" >/dev/null \ + || mount_msdos "${part}" "${mnt}" \ + || die "Could not mount ${part} at ${mnt}" +} mount_fat "${P1}" "${M1}" mount_fat "${P2}" "${M2}" @@ -463,11 +473,12 @@ cp "${KERNEL}" "${M1}/EFI/BOOT/BOOTX64.EFI" cp "${INITRAMFS}" "${M1}/initramfs.cpio.gz" # Boot piece: aesel by default (the Claude session that writes pieces this # machine runs). Override per-flash with AC_BOOT_PIECE (e.g. -# AC_BOOT_PIECE=notepat flash-mac.sh ...). The kernel resolves the name to -# /pieces/.mjs at boot — see the "Boot piece from config" block in -# ac-native.c. Devices already flashed keep whatever piece their -# /mnt/config.json names. -BOOT_PIECE="${AC_BOOT_PIECE:-aesel}" +# AC_BOOT_PIECE=notepat flash-mac.sh ...) or with usbConfig.piece in the +# inscription. The kernel resolves the name to /pieces/.mjs at boot — +# see the "Boot piece from config" block in ac-native.c. Devices already +# flashed keep whatever piece their /mnt/config.json names. +INSCRIBED_PIECE=$(node -e 'try { const c=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8")).usbConfig||{}; if (typeof c.piece === "string" && /^[a-z0-9-]+$/.test(c.piece)) process.stdout.write(c.piece); } catch {}' "${INSCRIPTION_FILE}") +BOOT_PIECE="${AC_BOOT_PIECE:-${INSCRIBED_PIECE:-aesel}}" # Write a device config.json. Base identity fields come from the shell vars # (set from the inscription OR the legacy API path); the boot-personalization @@ -485,7 +496,8 @@ write_device_config() { # $1=dest $2=udp(1=include udpMidiBroadcast) if (c.city) cfg.city = c.city; if (Array.isArray(c.colors) && c.colors.length) cfg.colors = c.colors; if (c.mood) cfg.mood = c.mood; - if (c.mono) cfg.mono = true; + if (typeof c.mono === "boolean") cfg.mono = c.mono; + if (["left", "right", "both"].includes(c.monoOutput)) cfg.monoOutput = c.monoOutput; } catch (e) { /* no inscription (anon/legacy) — base fields only */ } fs.writeFileSync(dest, JSON.stringify(cfg) + "\n"); ' "$1" "${USER_HANDLE}" "${BOOT_PIECE}" "${USER_SUB}" "${USER_EMAIL}" "$2" "${INSCRIPTION_FILE}" diff --git a/fedac/native/scripts/flash-usbs.sh b/fedac/native/scripts/flash-usbs.sh new file mode 100755 index 0000000000..9d2f8616ac --- /dev/null +++ b/fedac/native/scripts/flash-usbs.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# flash-usbs.sh — flash every attached external physical disk with the cached +# AC OS release in /tmp/ac-os-pull. Dry run by default: lists the disks it +# would WIPE. Pass --go to flash them all, one after another. +# Disks over 256 GB are skipped unless --big is also passed (backup-drive guard). +set -uo pipefail +SRC=/tmp/ac-os-pull +FLASH=/Users/jas/aesthetic-computer/fedac/native/scripts/flash-mac.sh +LOG="${LOG:-$HOME/.ac-os/flash-six.log}" +mkdir -p "$(dirname "$LOG")" +GO=0; BIG=0 +for a in "$@"; do [ "$a" = "--go" ] && GO=1; [ "$a" = "--big" ] && BIG=1; done +[ -f "$SRC/vmlinuz" ] && [ -f "$SRC/initramfs.cpio.gz" ] || { echo "cache missing in $SRC — run: ac-os pull"; exit 1; } +DISKS=$(diskutil list external physical 2>/dev/null | awk '/^\/dev\/disk/{print $1}') +[ -z "$DISKS" ] && { echo "no external disks attached"; exit 1; } +TARGETS=() +for d in $DISKS; do + info=$(diskutil info "$d") + name=$(echo "$info" | awk -F': *' '/Device \/ Media Name/{print $2}') + bytes=$(echo "$info" | awk -F'[()]' '/Disk Size/{print $2}' | awk '{print $1}') + gb=$(( ${bytes:-0} / 1000000000 )) + removable=$(echo "$info" | awk -F': *' '/Removable Media/{print $2}') + vols=$(diskutil list "$d" | awk 'NR>2 && $NF ~ /disk/ {print $(NF-3)}' | tr '\n' ',' | sed 's/,$//') + flag="" + if [ "$gb" -gt 256 ] && [ "$BIG" -ne 1 ]; then flag=" SKIP (>256 GB, pass --big)"; else TARGETS+=("$d"); fi + printf '%-12s %-28s %5s GB removable=%-3s vols=%s%s\n' "$d" "$name" "$gb" "$removable" "$vols" "$flag" +done +[ "$GO" -eq 1 ] || { echo; echo "dry run — pass --go to ERASE and flash the ${#TARGETS[@]} disk(s) above"; exit 0; } +ok=0; bad=0 +for d in "${TARGETS[@]}"; do + echo; echo "=== $(date '+%H:%M:%S') flashing $d" + if sudo "$FLASH" "$d" "$SRC" >>"$LOG" 2>&1; then + echo "$(date '+%H:%M:%S') DONE $d"; ok=$((ok+1)) + echo "$(date -u +%FT%TZ) DONE $d" >> "$LOG" + else + echo "$(date '+%H:%M:%S') FAILED $d — see $LOG"; bad=$((bad+1)); tail -8 "$LOG" + fi +done +echo; echo "flashed $ok, failed $bad, log: $LOG" +[ "$bad" -eq 0 ] -- 2.51.2