Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
Shell
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200#!/bin/bash# Bootstrap a lightweight Mac (Apple Silicon or Intel) to run aesthetic-computer# natively — outside the devcontainer — with all `ac-*` fish commands available.## Matches plans/MAC-NATIVE-DEVENV-PLAN.md. Safe to re-run (idempotent).# Prereqs: macOS 11+, Xcode CLT (git), the aesthetic-computer repo checked# out at $HOME/aesthetic-computer, the vault at $HOME/aesthetic-computer/aesthetic-computer-vault.## This script does NOT unlock the vault — do that manually with# `fish aesthetic-computer-vault/vault-tool.fish unlock` before or after.
set -euo pipefail
AC_ROOT="$HOME/aesthetic-computer"VAULT="$AC_ROOT/aesthetic-computer-vault"ASKPASS="/tmp/ac-askpass.sh"SUDOERS_FILE="/etc/sudoers.d/claude-ac-setup"
step() { printf "\n\033[1;34m▶ %s\033[0m\n" "$*"; }ok() { printf " \033[1;32m✓\033[0m %s\n" "$*"; }warn() { printf " \033[1;33m!\033[0m %s\n" "$*"; }die() { printf " \033[1;31m✗\033[0m %s\n" "$*"; exit 1; }
[[ "$(uname)" == "Darwin" ]] || die "this script is macOS-only"[[ -d "$AC_ROOT" ]] || die "aesthetic-computer repo not found at $AC_ROOT"
# -----------------------------------------------------------------------------step "1. GUI askpass helper for sudo -A"# -----------------------------------------------------------------------------cat > "$ASKPASS" <<'EOF'#!/bin/bash/usr/bin/osascript -e 'display dialog "Bootstrap needs sudo — enter your password:" default answer "" with hidden answer with icon caution' -e 'text returned of result' 2>/dev/nullEOFchmod +x "$ASKPASS"export SUDO_ASKPASS="$ASKPASS"sudo -A -vok "sudo primed via askpass"
# -----------------------------------------------------------------------------step "2. Homebrew"# -----------------------------------------------------------------------------if ! command -v brew >/dev/null 2>&1 && ! [[ -x /opt/homebrew/bin/brew ]]; then NONINTERACTIVE=1 /bin/bash -c \ "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"fieval "$(/opt/homebrew/bin/brew shellenv)"ok "brew at $(which brew)"
# -----------------------------------------------------------------------------step "3. Brew formulas"# -----------------------------------------------------------------------------# Core — needed for fish, node, mkcert, site toolingbrew install --quiet fish fnm mkcert nss jq ripgrep bat gh tree \ coreutils gnu-sed wget nmap ffmpeg \ caddy ngrok/ngrok/ngrok redis \ stripe/stripe-cli/stripe doctl awscli \ gnupg pinentry-mac 2>&1 | tail -3ok "core brew formulas installed"
# -----------------------------------------------------------------------------step "4. /workspaces → /Users/$USER via synthetic.conf"# -----------------------------------------------------------------------------if [[ -L /workspaces ]] && [[ "$(readlink /workspaces)" == "/Users/$USER" ]]; then ok "/workspaces already symlinked"else # macOS SIP prevents writes to /, but synthetic.conf is the sanctioned way. printf 'workspaces\t/Users/%s\n' "$USER" | sudo -A tee /etc/synthetic.conf >/dev/null sudo -A chmod 644 /etc/synthetic.conf sudo -A /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t || \ warn "apfs.util trigger failed — a reboot will also apply synthetic.conf" if [[ -L /workspaces ]]; then ok "/workspaces → $(readlink /workspaces)" else warn "/workspaces not yet present; reboot to activate" fifi
# -----------------------------------------------------------------------------step "5. Scoped NOPASSWD sudoers"# -----------------------------------------------------------------------------SUDOERS_TMP=$(mktemp)cat > "$SUDOERS_TMP" <<EOF# Claude Code / aesthetic-computer native dev — narrow NOPASSWD for recurring# ops. Review with \`sudo visudo -c\`.Cmnd_Alias AC_DEV_SETUP = \\ /usr/bin/tee -a /etc/shells, \\ /usr/bin/security, \\ /opt/homebrew/bin/mkcert
$USER ALL=(root) NOPASSWD: AC_DEV_SETUPEOFsudo -A visudo -cf "$SUDOERS_TMP" >/dev/nullsudo -A install -o root -g wheel -m 0440 "$SUDOERS_TMP" "$SUDOERS_FILE"rm -f "$SUDOERS_TMP"ok "sudoers file installed at $SUDOERS_FILE"
# -----------------------------------------------------------------------------step "6. fnm + Node (24.18.1 & legacy Nanos 20.5.0)"# -----------------------------------------------------------------------------eval "$(fnm env --shell bash)"fnm install 24.18.1fnm install 20.5.0fnm default 24.18.1fnm use 24.18.1ok "node $(node --version) via fnm ($(fnm current))"
# -----------------------------------------------------------------------------step "7. Global npm CLIs (incl. Claude Code)"# -----------------------------------------------------------------------------npm i -g --silent \ @anthropic-ai/claude-code \ @devcontainers/cli \ netlify-cli \ prettier typescript typescript-language-server \ concurrently kill-port http-server npm-check-updates 2>&1 | tail -1ok "npm globals installed"
# Native Claude Code binary (matches Dockerfile:223)if ! [[ -x "$HOME/.local/bin/claude" ]]; then curl -fsSL https://claude.ai/install.sh | bash >/dev/nullfiok "claude native: $("$HOME/.local/bin/claude" --version | head -1)"
# -----------------------------------------------------------------------------step "8. fish as default login shell"# -----------------------------------------------------------------------------if ! grep -q "/opt/homebrew/bin/fish" /etc/shells; then echo "/opt/homebrew/bin/fish" | sudo -n tee -a /etc/shells >/dev/nullfiCURRENT_SHELL=$(dscl . -read "/Users/$USER" UserShell | awk '{print $2}')if [[ "$CURRENT_SHELL" != "/opt/homebrew/bin/fish" ]]; then sudo -A /usr/bin/dscl . -change "/Users/$USER" UserShell "$CURRENT_SHELL" /opt/homebrew/bin/fishfiok "login shell: $(dscl . -read /Users/$USER UserShell | awk '{print $2}')"
# -----------------------------------------------------------------------------step "9. ~/.config/fish/config.fish"# -----------------------------------------------------------------------------bash "$AC_ROOT/scripts/mac-family-setup.sh" --dotfiles-onlyok "shared native fish and editor dotfiles linked"
# -----------------------------------------------------------------------------step "10. GPG agent with pinentry-mac"# -----------------------------------------------------------------------------mkdir -p "$HOME/.gnupg"chmod 700 "$HOME/.gnupg"if ! grep -q pinentry-mac "$HOME/.gnupg/gpg-agent.conf" 2>/dev/null; then cat >> "$HOME/.gnupg/gpg-agent.conf" <<'EOF'pinentry-program /opt/homebrew/bin/pinentry-macdefault-cache-ttl 3600max-cache-ttl 7200EOFfigpgconf --kill gpg-agent >/dev/null 2>&1 || truegpgconf --launch gpg-agentok "gpg-agent uses pinentry-mac"
# -----------------------------------------------------------------------------step "11. mkcert CA + localhost dev certs"# -----------------------------------------------------------------------------mkcert -install >/dev/null 2>&1ok "mkcert CA installed in System keychain"cd "$AC_ROOT/ssl-dev"if ! [[ -f localhost.pem ]]; then env nogreet=true /opt/homebrew/bin/fish ./ssl-install.fish >/dev/null 2>&1fiok "ssl-dev/localhost.pem ($(date -r localhost.pem +%Y-%m-%d))"
# -----------------------------------------------------------------------------step "12. Vault env links"# -----------------------------------------------------------------------------# session-server reads .env relative to its own dirif [[ -f "$VAULT/session-server/.env" ]]; then ln -sfn "$VAULT/session-server/.env" "$AC_ROOT/session-server/.env" ok "session-server/.env linked from vault"else warn "vault locked? $VAULT/session-server/.env missing"fi# system/.env is loaded by ac-lith if present — no default, optional for local dev
# -----------------------------------------------------------------------------step "13. Smoke test: boot ac-site briefly"# -----------------------------------------------------------------------------kill-port 8888 >/dev/null 2>&1 || true(cd "$AC_ROOT/lith" && node server.mjs >/tmp/ac-bootstrap-lith.log 2>&1 &)LITH_PID=$!sleep 4if curl -sSI --fail https://localhost:8888/ -o /dev/null 2>/dev/null; then ok "ac-site responds on https://localhost:8888 with trusted cert"else warn "ac-site smoke test failed — see /tmp/ac-bootstrap-lith.log"fikill "$LITH_PID" 2>/dev/null || truekill-port 8888 >/dev/null 2>&1 || true
# -----------------------------------------------------------------------------printf "\n\033[1;32m✓ Bootstrap complete.\033[0m\n"printf " Open a new Terminal tab (fish will be the default).\n"printf " Run \`ac-help\` to list commands, then \`ac-site\` to boot the site.\n\n"