Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
41 kB · 1018 lines
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019#!/usr/bin/env fish
# Log file for aesthetic function to tail during waitset -g ENTRY_LOG /tmp/entry-fish.logset -g ENTRY_LOG_DIR /home/me/.entry-logsif test -d /workspaces/aesthetic-computer/.devcontainer set -g ENTRY_LOG_DIR /workspaces/aesthetic-computer/.devcontainer/entry-logsendset -l entry_ts (date +%Y%m%d_%H%M%S)set -g ENTRY_LOG_REAL $ENTRY_LOG_DIR/entry-$entry_ts.log
mkdir -p $ENTRY_LOG_DIRecho "" > $ENTRY_LOG_REALln -sf $ENTRY_LOG_REAL $ENTRY_LOGln -sf $ENTRY_LOG_REAL $ENTRY_LOG_DIR/latest.logecho "" > $ENTRY_LOG
# Aggressive logging function - writes immediately with timestamp and syncs to diskfunction log set -l timestamp (date '+%H:%M:%S.%3N') set -l msg "[$timestamp] $argv" builtin echo $msg builtin echo $msg >> $ENTRY_LOG_REAL 2>/dev/null # Force sync to disk immediately so we don't lose logs on crash sync 2>/dev/nullend
# Log with specific level prefixfunction log_info log "ℹ️ $argv"end
function log_ok log "✅ $argv"end
function log_warn log "⚠️ $argv"end
function log_error log "❌ $argv"end
function log_step log "🔹 $argv"end
# Simple wrapper: append each echo to log file too (keep for backward compat)# We override echo within this scriptfunction echo builtin echo $argv builtin echo "[" (date '+%H:%M:%S') "] $argv" >> $ENTRY_LOG_REAL 2>/dev/nullend
# Run a command and capture output to the entry log.function log_cmd set -l cmd_str (string join ' ' -- $argv) log_step "Running: $cmd_str" command $argv >> $ENTRY_LOG_REAL 2>&1 set -l cmd_status $status if test $cmd_status -ne 0 log_error "Command failed ($cmd_status): $cmd_str" else log_ok "Command succeeded: $cmd_str" end sync 2>/dev/null return $cmd_statusend
# Run a command with a timeout if available.function log_cmd_timeout set -l seconds $argv[1] set -l cmd $argv[2..-1] log_step "Running with "$seconds"s timeout: "(string join ' ' -- $cmd) if type -q timeout log_cmd timeout $seconds $cmd else log_cmd $cmd end return $statusend
# Start CDP tunnel without blocking startup if SSH auth is missing.function start_cdp_tunnel set -l ssh_args -f -N -o StrictHostKeyChecking=no -o BatchMode=yes -o ExitOnForwardFailure=yes -o ConnectTimeout=5 -o ConnectionAttempts=1 -L 9333:127.0.0.1:9333 me@host.docker.internal log_cmd_timeout 8 ssh $ssh_args return $statusend
log "════════════════════════════════════════════════════════════════"log "🚀 ENTRY.FISH STARTING"log "════════════════════════════════════════════════════════════════"log_info "Timestamp: "(date -Iseconds)log_info "Log file: $ENTRY_LOG_REAL"log_info "Log dir: $ENTRY_LOG_DIR"log_info "Hostname: "(cat /etc/hostname 2>/dev/null; or echo "unknown")log_info "User: "(whoami)log_info "PWD: "(pwd)
# 🔍 Start Devcontainer Status Server EARLY (so VS Code can see boot progress)# This allows the Welcome Panel to show the boot process in real-timelog_step "EARLY START: Devcontainer Status Server"if test -f /workspaces/aesthetic-computer/artery/devcontainer-status.mjs # Kill any existing instance first pkill -f "devcontainer-status" 2>/dev/null sleep 0.1 nohup node /workspaces/aesthetic-computer/artery/devcontainer-status.mjs --server >/tmp/devcontainer-status.log 2>&1 & disown log_ok "Status Server started early on http://127.0.0.1:7890"else log_warn "devcontainer-status.mjs not found, will try later"end
log_step "PHASE 1: Environment sync"
# --- Sync secrets from vault to devcontainer env ---# The vault stores the canonical secrets; copy them to devcontainer on startupset -l vault_env ~/aesthetic-computer-vault/.devcontainer/envs/devcontainer.envset -l devcontainer_env ~/aesthetic-computer/.devcontainer/envs/devcontainer.envif test -f $vault_env if test -f $devcontainer_env # Merge: update devcontainer.env with any keys from vault (vault wins) for line in (cat $vault_env) set -l key (string split -m1 '=' -- $line)[1] if test -n "$key" # Remove old key if exists, then append new sed -i "/^$key=/d" $devcontainer_env 2>/dev/null echo $line >> $devcontainer_env end end log_ok "Synced secrets from vault to devcontainer.env" else # No devcontainer.env, just copy from vault cp $vault_env $devcontainer_env log_ok "Copied secrets from vault to devcontainer.env" endelse log_warn "Vault env not found at $vault_env"end
# --- Setup session-server SSH key from vault ---set -l ss_key_src ~/aesthetic-computer-vault/session-server/session_serverset -l ss_key_dst ~/.ssh/session_serverif test -f $ss_key_src; and not test -f $ss_key_dst # Ensure .ssh directory exists with correct ownership and permissions sudo mkdir -p ~/.ssh sudo chown me:me ~/.ssh sudo chmod 700 ~/.ssh
cp $ss_key_src $ss_key_dst cp "$ss_key_src.pub" "$ss_key_dst.pub" chmod 600 $ss_key_dst chmod 644 "$ss_key_dst.pub"
# Create config file if it doesn't exist with correct ownership if not test -f ~/.ssh/config touch ~/.ssh/config chmod 600 ~/.ssh/config end
# Add SSH config entry if missing if not grep -q "Host session-server" ~/.ssh/config 2>/dev/null echo "" >> ~/.ssh/config echo "Host session-server" >> ~/.ssh/config echo " HostName 157.245.134.225" >> ~/.ssh/config echo " User root" >> ~/.ssh/config echo " IdentityFile ~/.ssh/session_server" >> ~/.ssh/config echo " IdentitiesOnly yes" >> ~/.ssh/config echo " StrictHostKeyChecking accept-new" >> ~/.ssh/config end log_ok "Session-server SSH key installed"else if not test -f $ss_key_src log_warn "Session-server SSH key not found in vault"end
log_step "PHASE 2: Checking for fast reload path"
# Fast path for VS Code "Reload Window" - if .waiter exists and key processes running, skip setupif test -f /home/me/.waiter log_info "Detected reload (found .waiter file)"
# Check if key processes are already running set -l dockerd_running (pgrep -x dockerd 2>/dev/null) set -l emacs_running (pgrep -f "emacs.*daemon" 2>/dev/null)
if test -n "$dockerd_running" echo "✅ Docker daemon already running (PID: $dockerd_running)"
# Check if emacs daemon is running AND responsive set -l emacs_config /home/me/aesthetic-computer/dotfiles/dot_config/emacs.el if test -n "$emacs_running" if emacsclient -e t >/dev/null 2>&1 echo "✅ Emacs daemon running and responsive (PID: $emacs_running)" else echo "⚠️ Emacs daemon zombie detected (PID: $emacs_running) - restarting..." pkill -9 -f "emacs.*daemon" 2>/dev/null pkill -9 emacs 2>/dev/null sleep 1 emacs -q --daemon -l $emacs_config 2>&1 sleep 1 if emacsclient -e t >/dev/null 2>&1 echo "✅ Emacs daemon restarted successfully" else echo "❌ Failed to restart emacs daemon" end end else echo "⚠️ Emacs daemon not running - starting..." emacs -q --daemon -l $emacs_config 2>&1 sleep 1 end
# Ensure CDP tunnel is up (for artery-tui to control VS Code on host) set -l cdp_tunnel (pgrep -f "ssh.*9333.*host.docker.internal" 2>/dev/null) if test -n "$cdp_tunnel" echo "✅ CDP tunnel already running (PID: $cdp_tunnel)" else echo "🩸 Starting CDP tunnel..." start_cdp_tunnel if test $status -eq 0 echo "✅ CDP tunnel established (localhost:9333 → host:9333)" else echo "⚠️ CDP tunnel failed (artery may not control VS Code)" end end
echo "⚡ Skipping full setup - container already configured" echo "✅ Ready! (fast reload path)" exit 0 else echo "⚠️ Docker daemon not found, running full setup..." endend
# Fix fish path if needed (Fedora installs to /usr/sbin instead of /usr/bin)if not test -f /usr/bin/fish; and test -f /usr/sbin/fish sudo ln -sf /usr/sbin/fish /usr/bin/fish echo "🐟 Created fish symlink: /usr/bin/fish -> /usr/sbin/fish"end
# Function to ensure fish config directory has correct permissionsfunction ensure_fish_config_permissions set -l fish_config_dir /home/me/.config/fish set -l fish_data_dir /home/me/.local/share/fish
echo "🔧 Fixing permissions for fish directories..."
# Fix the parent .config directory first (most important) if test -d /home/me/.config sudo chown -R me:me /home/me/.config 2>/dev/null sudo chmod -R 755 /home/me/.config 2>/dev/null echo "✅ Fixed permissions for /home/me/.config" end
if test -d $fish_config_dir # Fix ownership and permissions for fish config directory - be VERY aggressive sudo chown -R me:me $fish_config_dir 2>/dev/null sudo chmod -R 755 $fish_config_dir 2>/dev/null sudo chmod 644 $fish_config_dir/*.fish 2>/dev/null # Also fix individual subdirectories explicitly sudo chown -R me:me $fish_config_dir/conf.d 2>/dev/null sudo chown -R me:me $fish_config_dir/functions 2>/dev/null sudo chown -R me:me $fish_config_dir/completions 2>/dev/null echo "✅ Fixed permissions for $fish_config_dir" end
if test -d $fish_data_dir # Fix ownership and permissions for fish data directory sudo chown -R me:me $fish_data_dir 2>/dev/null sudo chmod -R 755 $fish_data_dir 2>/dev/null echo "✅ Fixed permissions for $fish_data_dir" end
# Fix /home/me/.local directory too if test -d /home/me/.local sudo chown -R me:me /home/me/.local 2>/dev/null sudo chmod -R 755 /home/me/.local 2>/dev/null echo "✅ Fixed permissions for /home/me/.local" end
# Disable fish's universal variable file daemon (fishd) which causes permission issues # We'll use fish_variables instead which is simpler and doesn't create temp files set -U fish_greeting "" # Suppress greeting while we're at it
echo "✨ All permissions fixed!"end
# Function to ensure correct Docker socket permissionsfunction ensure_docker_socket_permissions set -l DOCKER_SOCKET /var/run/docker.sock if test -S $DOCKER_SOCKET set -l DOCKER_GID (stat -c '%g' $DOCKER_SOCKET) set -l CURRENT_DOCKER_GROUP_INFO (getent group $DOCKER_GID)
if test -z "$CURRENT_DOCKER_GROUP_INFO" # No group with this GID exists if getent group docker > /dev/null echo "Modifying existing docker group to GID $DOCKER_GID..." sudo groupmod -g $DOCKER_GID docker else echo "Creating new docker group with GID $DOCKER_GID..." sudo groupadd -g $DOCKER_GID docker end else # A group with this GID already exists, check if it's named 'docker' set -l GROUP_NAME (echo $CURRENT_DOCKER_GROUP_INFO | cut -d: -f1) if test "$GROUP_NAME" != "docker" echo "Warning: Group with GID $DOCKER_GID exists but is named '$GROUP_NAME', not 'docker'. Manual intervention might be needed." # Optionally, you could decide to add 'me' to this group anyway, # or try to rename it if it's safe, or delete and recreate 'docker' group. # For now, we'll proceed assuming 'docker' is the target group name. if getent group docker > /dev/null echo "Modifying existing docker group to GID $DOCKER_GID..." sudo groupmod -g $DOCKER_GID docker else echo "Creating new docker group with GID $DOCKER_GID..." sudo groupadd -g $DOCKER_GID docker end else echo "Docker group '$GROUP_NAME' with GID $DOCKER_GID already exists." end end
# Ensure 'me' user is in the 'docker' group if not groups me | string match -q -r '\\bdocker\\b' echo "Adding user 'me' to docker group..." sudo usermod -aG docker me # Changes to group membership may require a new login session or `newgrp docker` # to take effect immediately in the current shell. # For a script, subsequent commands in this same script might not see the new group immediately. # However, for processes spawned after this script, it should be fine. else echo "User 'me' is already in docker group." end else echo "Docker socket $DOCKER_SOCKET not found." endend
function install_and_trust_certificate set -l certificate_path $argv[1] set -l certificate_key_path $argv[2] set -l certificate_dir /etc/pki/ca-trust/source/anchors/
if test -z "$certificate_path"; or test -z "$certificate_key_path" return end
if test -f $certificate_path sudo cp $certificate_path $certificate_dir end
if test -f $certificate_key_path sudo cp $certificate_key_path $certificate_dir end
sudo update-ca-trustend
function ensure_ssl_dev_certs set -l ssl_dir /home/me/aesthetic-computer/ssl-dev set -l nanos_ssl_dir /home/me/aesthetic-computer/nanos/ssl
if not test -d $ssl_dir return end
if not type -q mkcert echo "⚠️ mkcert not available; skipping automatic certificate generation." return end
set -l previous_dir (pwd) cd $ssl_dir
set -l have_cert 1 if not test -f localhost.pem; or not test -f localhost-key.pem set have_cert 0 end
if test $have_cert -eq 0 echo "🔐 Generating mkcert certificates for the dev container..." if not test -d /home/me/.local/share/mkcert echo "📥 Installing mkcert local CA..." log_cmd_timeout 60 mkcert -install if test $status -ne 0 echo "⚠️ mkcert CA installation failed. See entry.fish log for details." end if not type -q certutil echo "📦 Installing nss-tools for certificate trust..." log_cmd_timeout 300 sudo dnf install -y nss-tools log_cmd_timeout 60 mkcert -install end end
log_cmd_timeout 120 env nogreet=true fish ./ssl-install.fish if test $status -ne 0 echo "⚠️ Automatic certificate generation failed using ssl-install.fish." cd $previous_dir return end else log_cmd_timeout 120 env nogreet=true fish ./ssl-install.fish --install-only end
mkdir -p $nanos_ssl_dir cp localhost.pem $nanos_ssl_dir/localhost.pem cp localhost-key.pem $nanos_ssl_dir/localhost-key.pem
sudo chown me:me -R $ssl_dir sudo chown me:me -R $nanos_ssl_dir
install_and_trust_certificate $ssl_dir/localhost.pem $ssl_dir/localhost-key.pem
cd $previous_dirend
log_step "PHASE 3: Docker socket permissions"# Call the function to set up Docker socket permissionsensure_docker_socket_permissions
log_step "PHASE 4: Starting daemons"# Start Docker daemon in the background if not already runningif not pgrep -x dockerd >/dev/null log_info "Starting Docker daemon..." sudo dockerd >/tmp/dockerd.log 2>&1 &else log_ok "Docker daemon already running."end
# Start Ollama daemon in the background if available and not already runningif type -q ollama if not pgrep -x ollama >/dev/null log_info "Starting Ollama daemon..." ollama serve >/tmp/ollama.log 2>&1 & else log_ok "Ollama daemon already running." endelse log_warn "Ollama not found; skipping."end
log_step "PHASE 4b: Cockpit web interface"# Start Cockpit web server (no TLS for local dev, port 9090)# Terminal + system overview work without systemd; service/journal features need systemdset -l cockpit_ws_bin ""for p in /usr/lib/cockpit/cockpit-ws /usr/libexec/cockpit-ws if test -x $p set cockpit_ws_bin $p break endend
if test -n "$cockpit_ws_bin" if not pgrep -f "cockpit-ws" >/dev/null # Set a dev password so PAM auth works (use vault COCKPIT_PASSWORD if set) set -l cockpit_pass (test -n "$COCKPIT_PASSWORD"; and echo $COCKPIT_PASSWORD; or echo "aesthetic") echo "me:$cockpit_pass" | sudo chpasswd 2>/dev/null sudo $cockpit_ws_bin --no-tls --port=9090 >/tmp/cockpit-ws.log 2>&1 & disown log_ok "Cockpit started → http://localhost:9090 (login: me / $cockpit_pass)" else log_ok "Cockpit already running" endelse log_warn "Cockpit not installed — skipping (rebuild container to add it)"end
log_step "PHASE 5: Environment setup"# Ensure the envs directory exists and is accessible (fallback if mount fails)if not test -d /home/me/envs log_info "Creating missing /home/me/envs directory and linking to .devcontainer/envs" mkdir -p /home/me/envs ln -sf /workspaces/aesthetic-computer/.devcontainer/envs/* /home/me/envs/end
if test -d /home/me/envs source /home/me/envs/load_envs.fish load_envs # Load devcontainer envs conditionally. echo "🔧 Environment variables loaded from devcontainer.env"end
# Set environment variables to prevent ETXTBSY errors and disable telemetryset -gx NETLIFY_CLI_TELEMETRY_DISABLED 1set -gx NODE_DISABLE_COMPILE_CACHE 1
set -gx TERM xterm-256color
# Create xdg-open wrapper to open URLs on Windows host from dev containerif not test -f /usr/local/bin/xdg-open echo "🌐 Creating xdg-open wrapper for host browser..." printf '#!/bin/bash\n"\$BROWSER" "\$@"\n' | sudo tee /usr/local/bin/xdg-open >/dev/null sudo chmod +x /usr/local/bin/xdg-openend
# Start dbus-run-session for the keryring and execute the command passed to the script# dbus-run-session -- sh -c "eval \$(echo \$DBUS_SESSION_BUS_ADDRESS); exec $argv"
# Send a welcome message!toilet "Aesthetic Computer" -f future | lolcat -x -r
# Go to the user's directory.cd /home/me
log_step "PHASE 6: SSL certificates"ensure_ssl_dev_certs
log_step "PHASE 7: GitHub authentication"# Login to Github - use GH_TOKEN from vault if availableset -l gh_authenticated 1if not gh auth status >/dev/null 2>&1 if test -n "$GH_TOKEN" log_info "Authenticating to GitHub using GH_TOKEN..." echo $GH_TOKEN | gh auth login --with-token if gh auth status >/dev/null 2>&1 log_ok "GitHub authentication successful" else log_error "GitHub authentication failed" log_warn "Continuing without GitHub auth; vault/git operations may be limited." set gh_authenticated 0 end else log_warn "Not logged into GitHub and no GH_TOKEN available." log_warn "Continuing startup without GitHub auth." set gh_authenticated 0 endelse log_ok "Already authenticated to GitHub"end
log_step "PHASE 8: Git configuration"if test -n "$GIT_USER_EMAIL" git config --global user.email $GIT_USER_EMAIL log_info "Set git user email to: $GIT_USER_EMAIL"end
if test -n "$GIT_USER_NAME" git config --global user.name $GIT_USER_NAME log_info "Set git user name to: $GIT_USER_NAME"end
# Add aesthetic-computer as the "safe" directory.git config --global --add safe.directory /home/me/aesthetic-computer
# Set rebase as default for pull operations.git config --global pull.rebase true
# Disable GPG signing to prevent commit issues in dev environmentgit config --global commit.gpgsign false
# Make sure git is setup and authorized for making commits via `gh`.if test $gh_authenticated -eq 1 gh auth setup-gitelse log_warn "Skipping 'gh auth setup-git' because GitHub auth is unavailable."end
# Ensure GPG signing is disabled at both global and local levels (GitHub CLI might re-enable it)git config --global commit.gpgsign falsecd /home/me/aesthetic-computergit config --local commit.gpgsign falsegit config --local user.signingkey ""echo "🔓 GPG signing disabled for commits"
# Disable Netlify CLI telemetry to prevent ETXTBSY errorsecho "Disabling Netlify CLI telemetry..."cd /home/me/aesthetic-computer/system && netlify --telemetry-disable 2>/dev/null || echo "Netlify telemetry disable completed"
# Add aesthetic.local and sotce.local to /etc/hosts if they don't already existif not grep -q "aesthetic.local" /etc/hosts echo "127.0.0.1 aesthetic.local" | sudo tee -a /etc/hostsend
if not grep -q "sotce.local" /etc/hosts echo "127.0.0.1 sotce.local" | sudo tee -a /etc/hostsend
log_step "PHASE 9: Vault setup"# Apply the 'vault' credentials to the mounted aesthetic-computer volume, and make sure it exists.if test -d /home/me/aesthetic-computer if not test -d /home/me/aesthetic-computer/aesthetic-computer-vault log_info "Cloning vault repository..." gh repo clone whistlegraph/aesthetic-computer-vault /home/me/aesthetic-computer/aesthetic-computer-vault cd /home/me/aesthetic-computer/aesthetic-computer-vault log_info "Running devault.fish..." sudo fish devault.fish
# Load environment variables after initial vault setup if test -d /home/me/envs source /home/me/envs/load_envs.fish load_envs # Load envs after initial vault setup log_ok "Environment variables loaded after initial vault setup" end else cd /home/me/aesthetic-computer/aesthetic-computer-vault log_info "Pulling latest vault..." git pull sudo fish devault.fish log_ok "Vault mounted." end
# Reload environment variables after vault is mounted if test -d /home/me/envs source /home/me/envs/load_envs.fish load_envs # Reload envs after vault mount log_ok "Environment variables reloaded after vault mount" end
# Setup SSH keys from vault (only if not already set up) set -l vault_ssh /home/me/aesthetic-computer/aesthetic-computer-vault/home/.ssh if test -d $vault_ssh # Only copy if id_rsa doesn't exist yet (first time setup) if not test -f /home/me/.ssh/id_rsa # Ensure .ssh directory exists with correct ownership and permissions sudo mkdir -p /home/me/.ssh sudo chown -R me:me /home/me/.ssh 2>/dev/null sudo chmod 700 /home/me/.ssh 2>/dev/null
cp -f $vault_ssh/* /home/me/.ssh/ 2>/dev/null chmod 700 /home/me/.ssh 2>/dev/null chmod 600 /home/me/.ssh/id_rsa 2>/dev/null chmod 644 /home/me/.ssh/id_rsa.pub 2>/dev/null chmod 600 /home/me/.ssh/config 2>/dev/null chmod 600 /home/me/.ssh/aesthetic_pds 2>/dev/null chmod 644 /home/me/.ssh/aesthetic_pds.pub 2>/dev/null log_ok "SSH keys restored from vault" else log_info "SSH keys already exist, skipping vault copy" end
# Always restore ~/.ssh/config from vault (even if keys already exist) # so that 'Host aesthetic' / 'Host windows-host' / etc. edits to the # tracked config propagate to running containers on subsequent starts. if test -f $vault_ssh/config cp -f $vault_ssh/config /home/me/.ssh/config 2>/dev/null chmod 600 /home/me/.ssh/config 2>/dev/null end end
# Ensure SSH bridge to Windows host (ac-ssh-bridge) is running on the # Fedora host's Docker daemon. The bridge is a --network=host alpine/socat # container that listens on :2222 on the Fedora side and forwards to # 172.19.64.1:22 (WSL2 NAT gateway to Windows sshd). This lets the # devcontainer reach Windows via 'ssh aesthetic' (see ~/.ssh/config). # See memory/ssh_bridge_to_windows.md for the full topology writeup. if test -S /var/run/docker.sock if sudo -n docker inspect -f '{{.State.Running}}' ac-ssh-bridge 2>/dev/null | string match -q true log_info "SSH bridge to Windows (ac-ssh-bridge) already running" else # Probe for the Windows host via the default gateway of a host-networked # helper container (that's Fedora's default route, which is the WSL2 gateway # = the Windows host from Fedora's namespace). Skip bridge setup if we # can't figure it out or if docker is unavailable. set -l win_ip (sudo -n docker run --rm --network=host alpine sh -c 'ip route | awk "/default/ {print \$3}"' 2>/dev/null) if test -n "$win_ip" sudo -n docker rm -f ac-ssh-bridge 2>/dev/null if sudo -n docker run -d --name ac-ssh-bridge --restart unless-stopped --network=host alpine/socat "TCP-LISTEN:2222,fork,reuseaddr" "TCP:$win_ip:22" >/dev/null 2>&1 log_ok "SSH bridge to Windows started (ac-ssh-bridge -> $win_ip:22)" else log_info "SSH bridge to Windows not started (docker run failed — Windows not reachable or non-standard topology)" end else log_info "SSH bridge to Windows not started (could not detect Windows host IP)" end end end
# Setup Copilot CLI config from vault (if volume is empty but vault has backup) set -l vault_copilot /home/me/aesthetic-computer/aesthetic-computer-vault/home/.copilot if test -d $vault_copilot # Only restore if volume mount is empty (no config.json) if not test -f /home/me/.copilot/config.json mkdir -p /home/me/.copilot cp -f $vault_copilot/config.json /home/me/.copilot/ 2>/dev/null log_ok "Copilot CLI config restored from vault" else log_info "Copilot CLI config already exists (using volume data)" end endelse log_error "Vault unmounted! /home/me/aesthetic-computer not found"end
log_step "PHASE 10: Fixing permissions"# Fix all permissions AFTER vault setup (vault copies files as root with sudo)log_info "Fixing permissions after vault setup..."
# First, ensure the home directory itself is owned by me (critical for .emacs-logs etc)sudo chown me:me /home/me 2>/dev/nulllog_ok "Fixed ownership of /home/me"
# Create .emacs-logs directory if it doesn't existmkdir -p /home/me/.emacs-logs 2>/dev/nulllog_ok "Ensured /home/me/.emacs-logs exists"
# Fix fish config permissions (vault may have overwritten with root-owned files)log_info "Fixing fish config permissions..."ensure_fish_config_permissions
# Fix SSH directory permissions (use sudo to fix ownership if needed)if test -d /home/me/.ssh if not test -O /home/me/.ssh # SSH dir owned by root (probably from vault setup), fix ownership sudo chown -R me:me /home/me/.ssh 2>/dev/null log_ok "Fixed SSH directory ownership" end chmod 700 /home/me/.ssh 2>/dev/null chmod 600 /home/me/.ssh/* 2>/dev/null chmod 644 /home/me/.ssh/*.pub 2>/dev/null log_ok "Fixed permissions for /home/me/.ssh"end
# Fix Copilot CLI directory permissions (volume mount may have root ownership)if test -d /home/me/.copilot sudo chown -R me:me /home/me/.copilot 2>/dev/null sudo chmod 700 /home/me/.copilot 2>/dev/null # Ensure pkg directory exists and is writable for package extraction mkdir -p /home/me/.copilot/pkg 2>/dev/null chmod 755 /home/me/.copilot/pkg 2>/dev/null sudo chmod 600 /home/me/.copilot/config.json 2>/dev/null echo "✅ Fixed permissions for /home/me/.copilot (Copilot CLI config)"end
# Fix Claude/Codex config directory permissions after bind mounts are attached.# On Linux hosts, these mounts keep host ownership, so UID remapping is the main# fix; this container-side pass normalizes modes when ownership is already writable.for dir in /home/me/.claude /home/me/.codex if test -d $dir sudo chown -R me:me $dir 2>/dev/null chmod 700 $dir 2>/dev/null find $dir -type d -exec chmod 700 {} + 2>/dev/null find $dir -type f -exec chmod 600 {} + 2>/dev/null echo "✅ Fixed permissions for $dir" endend
if test -f /home/me/.claude.json sudo chown me:me /home/me/.claude.json 2>/dev/null chmod 600 /home/me/.claude.json 2>/dev/null echo "✅ Fixed permissions for /home/me/.claude.json"end
# Fix VS Code extension SecretStorage persistence paths.for dir in /home/me/.config/Code/User/globalStorage /home/me/.config/Code/User/workspaceStorage if test -d $dir sudo chown -R me:me $dir 2>/dev/null find $dir -type d -exec chmod 700 {} + 2>/dev/null find $dir -type f -exec chmod 600 {} + 2>/dev/null echo "✅ Fixed permissions for $dir" endend
if test -d /home/me/.local/share/keyrings sudo chown -R me:me /home/me/.local/share/keyrings 2>/dev/null chmod 700 /home/me/.local/share/keyrings 2>/dev/null find /home/me/.local/share/keyrings -type f -exec chmod 600 {} + 2>/dev/null echo "✅ Fixed permissions for /home/me/.local/share/keyrings"end
# --- GPG agent: cache passphrase for the entire container lifetime ---mkdir -p /home/me/.gnupg 2>/dev/nullchmod 700 /home/me/.gnupgprintf "allow-loopback-pinentry\npinentry-program /usr/sbin/pinentry-curses\ndefault-cache-ttl 999999\nmax-cache-ttl 999999\n" > /home/me/.gnupg/gpg-agent.confgpgconf --reload gpg-agent 2>/dev/nulllog_ok "GPG agent configured (passphrase cached for container lifetime)"
cd /home/me/aesthetic-computer/aesthetic-computer-vaultgit pull
# Function to check and install npm dependencies in a directoryset -g NODE_DEPS_CHECK_SCRIPT /workspaces/aesthetic-computer/.devcontainer/scripts/check-node-deps.mjs
function verify_npm_versions set -l dir $argv[1] set -l dir_name (basename $dir)
if not test -f $NODE_DEPS_CHECK_SCRIPT return end
if not test -f $dir/package.json return end
if not test -d $dir/node_modules return end
log_cmd node $NODE_DEPS_CHECK_SCRIPT $dir set -l check_status $status
if test $check_status -ne 0 echo "♻️ Resyncing dependencies in $dir_name to match package.json versions..." cd $dir log_cmd npm ci --no-fund --no-audit if test $status -ne 0 echo "⚠️ npm ci failed in $dir_name, falling back to npm install" log_cmd npm install --no-fund --no-audit end endend
# Function to check and fix esbuild architecture mismatchesfunction fix_esbuild_architecture set -l dir $argv[1] set -l dir_name (basename $dir)
# Check if this directory has esbuild installed if test -d $dir/node_modules/@esbuild set -l arch (uname -m) set -l expected_esbuild
# Determine expected esbuild platform package if test "$arch" = "x86_64" set expected_esbuild "linux-x64" else if test "$arch" = "aarch64" set expected_esbuild "linux-arm64" else return 0 # Unknown architecture, skip end
# Check what's actually installed set -l installed_arch (ls $dir/node_modules/@esbuild 2>/dev/null | grep -E "^linux-")
if test -n "$installed_arch"; and test "$installed_arch" != "$expected_esbuild" echo "🔧 Wrong esbuild architecture in $dir_name: $installed_arch (need $expected_esbuild)" echo " Reinstalling dependencies to fix..." cd $dir rm -rf node_modules package-lock.json log_cmd npm install --no-fund --no-audit if test $status -eq 0 echo "✅ Fixed esbuild architecture in $dir_name" else echo "⚠️ Failed to fix esbuild in $dir_name" end end endend
function install_npm_deps set -l dir $argv[1] set -l dir_name (basename $dir)
if test -f $dir/package.json if not test -d $dir/node_modules || not count $dir/node_modules/* >/dev/null 2>/dev/null echo "📦 Installing dependencies in $dir_name..." cd $dir log_cmd npm ci --no-fund --no-audit if test $status -ne 0 echo "⚠️ Failed to install dependencies in $dir_name, trying npm install..." log_cmd npm install --no-fund --no-audit end else echo "✅ $dir_name already has node_modules" # Check for architecture mismatches even if node_modules exists fix_esbuild_architecture $dir end verify_npm_versions $dir endend
log_step "PHASE 11: NPM dependencies"# Check and install dependencies in key directoriescd /home/me/aesthetic-computer
# Install root dependencies firstif not test -d /home/me/aesthetic-computer/node_modules || not count /home/me/aesthetic-computer/node_modules/* >/dev/null log_info "Installing root dependencies..." log_cmd npm install -g npm@latest --no-fund --no-audit log_cmd npm ci --no-fund --no-auditelse log_ok "Root directory already has node_modules"end
log_info "Verifying npm versions..."verify_npm_versions /home/me/aesthetic-computer
# Install dependencies in critical subdirectories (archive intentionally excluded)set -l critical_dirs system session-server vscode-extension nanos daemon utilities shared
log_info "Installing dependencies in critical directories..."for dir in $critical_dirs if test -d /home/me/aesthetic-computer/$dir log_info "Processing $dir..." install_npm_deps /home/me/aesthetic-computer/$dir endend
# Run the comprehensive install script for any remaining directorieslog_info "Running comprehensive install script for remaining directories..."pushd /home/me/aesthetic-computerlog_cmd npm run install:everything-elsepopd
log_step "PHASE 12: Final setup"# Make sure this user owns the emacs directory.sudo chown -R me:me ~/.emacs.d
# Link the prompts directory to VSCode User configmkdir -p ~/.config/Code/Userrm -rf ~/.config/Code/User/promptsln -s /home/me/aesthetic-computer/prompts ~/.config/Code/User/promptslog_ok "Prompts directory linked to VSCode User config"
# Link the modes directory to .github for VSCode chatmodesrm -rf /home/me/aesthetic-computer/.githubln -s /home/me/aesthetic-computer/modes /home/me/aesthetic-computer/.githublog_ok "Modes directory linked to .github for VSCode chatmodes"
# Source the devcontainer config to load AC development functionssource /workspaces/aesthetic-computer/.devcontainer/config.fishlog_ok "AC development functions loaded (ac-pack, ac-unpack, etc.)"
# Copy feed secrets from vault to environment (if vault exists)if test -f /workspaces/aesthetic-computer/aesthetic-computer-vault/feed/.env log_info "Loading feed system environment variables from vault..." # Copy to devcontainer envs directory for persistence cp /workspaces/aesthetic-computer/aesthetic-computer-vault/feed/.env /home/me/envs/feed.env # Source it globally for the session for line in (cat /workspaces/aesthetic-computer/aesthetic-computer-vault/feed/.env | grep -v '^#' | grep '=') set -gx (string split '=' $line) end log_ok "Feed environment variables loaded"else echo "⚠️ Feed vault not found - skipping feed environment setup"end
# Set port 8888 to public in Codespaces (if applicable)if test -f /workspaces/aesthetic-computer/.devcontainer/scripts/set-port-public.fish log_cmd fish /workspaces/aesthetic-computer/.devcontainer/scripts/set-port-public.fishend
# Create .waiter file to signal container is ready (moved from postAttachCommand to avoid terminal popup in Codespaces)sudo touch /home/me/.waitersudo chmod 777 /home/me/.waitersudo chmod +w /home/meecho "✅ Container ready signal created (.waiter)"
# NOTE: Emacs daemon is NOT pre-started here. The `aesthetic` function# (called by the VS Code "💻 Aesthetic" task) unconditionally kills any# running daemon and starts fresh via `ensure-emacs-daemon-ready`.# Pre-starting here caused a race condition: entry.fish would start a# daemon, then the VS Code task would immediately kill it and start# another, wasting resources and causing load spikes that made the# second daemon unresponsive to the crash monitor.log_step "PHASE 12b: Emacs daemon (deferred to VS Code task)"log_info "Emacs daemon start deferred - will be started by 'aesthetic' function"
# 🩸 Setup SSH tunnel for CDP (Chrome DevTools Protocol) to host# This allows artery-tui to control VS Code on the host machineif test -n "$HOST_IP"; or test (uname -s) != "Linux" # Kill any existing CDP tunnels pkill -f "ssh.*9333.*host.docker.internal" 2>/dev/null
# Create tunnel in background (port 9333 to avoid conflicts with svchost.exe on 9222) start_cdp_tunnel if test $status -eq 0 echo "🩸 CDP tunnel established (localhost:9333 → host:9333)" else echo "⚠️ CDP tunnel failed (artery may not work)" endend
# 🧊 Start TURN server for WebRTC relay (required for UDP in Docker)if which turnserver >/dev/null 2>&1 pkill -f "turnserver" 2>/dev/null sleep 0.5 # Get host LAN IP from vault/machines.json or use default set -l HOST_LAN_IP "192.168.1.127" set -l HOST_LAN_IP_SOURCE "default" if test -f /workspaces/aesthetic-computer/aesthetic-computer-vault/machines.json set -l detected_ip (cat /workspaces/aesthetic-computer/aesthetic-computer-vault/machines.json 2>/dev/null | jq -r '.["aesthetic-mac"].lan_ip // empty' 2>/dev/null) if test -n "$detected_ip" set HOST_LAN_IP $detected_ip set HOST_LAN_IP_SOURCE "vault" end end log_info "TURN host IP: $HOST_LAN_IP (source: $HOST_LAN_IP_SOURCE)" echo $HOST_LAN_IP > /tmp/host-lan-ip turnserver -c /workspaces/aesthetic-computer/.devcontainer/turnserver.conf --external-ip=$HOST_LAN_IP >> $ENTRY_LOG_REAL 2>&1 & disown log_ok "TURN server started (localhost:3478, external-ip: $HOST_LAN_IP)"else log_warn "coturn not installed, UDP may not work across networks"end
# echo "Initializing 📋 Clipboard Service" | lolcat -x -r
# ❤️🔥# TODO: This may not be the best method because cdocker aesthetic needs to be# running in the user environment that has the clipboard.
# Run the isomorphic_copy clipboard on the host.# Make sure the host allows ssh access from its own private key... `ssh-copy-id -i ~/.ssh/id_rsa.pub $USER@localhost`# ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $HOST_USER@172.17.0.1
# ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $HOST_USER@172.17.0.1 "cdocker aesthetic"
# 🔍 Ensure Devcontainer Status Server is still running (it was started early)log_info "Checking Devcontainer Status Server..."if pgrep -f "devcontainer-status" >/dev/null log_ok "Status Server still running on http://127.0.0.1:7890"else if test -f /workspaces/aesthetic-computer/artery/devcontainer-status.mjs log_warn "Status Server died, restarting..." nohup node /workspaces/aesthetic-computer/artery/devcontainer-status.mjs --server >/tmp/devcontainer-status.log 2>&1 & disown log_ok "Status Server restarted on http://127.0.0.1:7890"end
log "════════════════════════════════════════════════════════════════"log "🎉 ENTRY.FISH COMPLETED SUCCESSFULLY"log "════════════════════════════════════════════════════════════════"log_info "End time: "(date -Iseconds)log_info "Log file: $ENTRY_LOG_REAL"
# Auto-launch is handled by the VS Code task (💻 Aesthetic) which runs on folder open# The task calls aesthetic-launch.sh which properly launches the emacs client# Background launch from entry.fish doesn't work because emacsclient -nw needs an interactive terminallog_info "Container setup complete - VS Code task will launch aesthetic platform"