Monorepo for Aesthetic.Computer
aesthetic.computer
1#!/usr/bin/env fish
2
3# Log file for aesthetic function to tail during wait
4set -g ENTRY_LOG /tmp/entry-fish.log
5set -g ENTRY_LOG_DIR /home/me/.entry-logs
6if test -d /workspaces/aesthetic-computer/.devcontainer
7 set -g ENTRY_LOG_DIR /workspaces/aesthetic-computer/.devcontainer/entry-logs
8end
9set -l entry_ts (date +%Y%m%d_%H%M%S)
10set -g ENTRY_LOG_REAL $ENTRY_LOG_DIR/entry-$entry_ts.log
11
12mkdir -p $ENTRY_LOG_DIR
13echo "" > $ENTRY_LOG_REAL
14ln -sf $ENTRY_LOG_REAL $ENTRY_LOG
15ln -sf $ENTRY_LOG_REAL $ENTRY_LOG_DIR/latest.log
16echo "" > $ENTRY_LOG
17
18# Aggressive logging function - writes immediately with timestamp and syncs to disk
19function log
20 set -l timestamp (date '+%H:%M:%S.%3N')
21 set -l msg "[$timestamp] $argv"
22 builtin echo $msg
23 builtin echo $msg >> $ENTRY_LOG_REAL 2>/dev/null
24 # Force sync to disk immediately so we don't lose logs on crash
25 sync 2>/dev/null
26end
27
28# Log with specific level prefix
29function log_info
30 log "ℹ️ $argv"
31end
32
33function log_ok
34 log "✅ $argv"
35end
36
37function log_warn
38 log "⚠️ $argv"
39end
40
41function log_error
42 log "❌ $argv"
43end
44
45function log_step
46 log "🔹 $argv"
47end
48
49# Simple wrapper: append each echo to log file too (keep for backward compat)
50# We override echo within this script
51function echo
52 builtin echo $argv
53 builtin echo "[" (date '+%H:%M:%S') "] $argv" >> $ENTRY_LOG_REAL 2>/dev/null
54end
55
56# Run a command and capture output to the entry log.
57function log_cmd
58 set -l cmd_str (string join ' ' -- $argv)
59 log_step "Running: $cmd_str"
60 command $argv >> $ENTRY_LOG_REAL 2>&1
61 set -l cmd_status $status
62 if test $cmd_status -ne 0
63 log_error "Command failed ($cmd_status): $cmd_str"
64 else
65 log_ok "Command succeeded: $cmd_str"
66 end
67 sync 2>/dev/null
68 return $cmd_status
69end
70
71# Run a command with a timeout if available.
72function log_cmd_timeout
73 set -l seconds $argv[1]
74 set -l cmd $argv[2..-1]
75 log_step "Running with "$seconds"s timeout: "(string join ' ' -- $cmd)
76 if type -q timeout
77 log_cmd timeout $seconds $cmd
78 else
79 log_cmd $cmd
80 end
81 return $status
82end
83
84# Start CDP tunnel without blocking startup if SSH auth is missing.
85function start_cdp_tunnel
86 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
87 log_cmd_timeout 8 ssh $ssh_args
88 return $status
89end
90
91log "════════════════════════════════════════════════════════════════"
92log "🚀 ENTRY.FISH STARTING"
93log "════════════════════════════════════════════════════════════════"
94log_info "Timestamp: "(date -Iseconds)
95log_info "Log file: $ENTRY_LOG_REAL"
96log_info "Log dir: $ENTRY_LOG_DIR"
97log_info "Hostname: "(cat /etc/hostname 2>/dev/null; or echo "unknown")
98log_info "User: "(whoami)
99log_info "PWD: "(pwd)
100
101# 🔍 Start Devcontainer Status Server EARLY (so VS Code can see boot progress)
102# This allows the Welcome Panel to show the boot process in real-time
103log_step "EARLY START: Devcontainer Status Server"
104if test -f /workspaces/aesthetic-computer/artery/devcontainer-status.mjs
105 # Kill any existing instance first
106 pkill -f "devcontainer-status" 2>/dev/null
107 sleep 0.1
108 nohup node /workspaces/aesthetic-computer/artery/devcontainer-status.mjs --server >/tmp/devcontainer-status.log 2>&1 &
109 disown
110 log_ok "Status Server started early on http://127.0.0.1:7890"
111else
112 log_warn "devcontainer-status.mjs not found, will try later"
113end
114
115log_step "PHASE 1: Environment sync"
116
117# --- Sync secrets from vault to devcontainer env ---
118# The vault stores the canonical secrets; copy them to devcontainer on startup
119set -l vault_env ~/aesthetic-computer-vault/.devcontainer/envs/devcontainer.env
120set -l devcontainer_env ~/aesthetic-computer/.devcontainer/envs/devcontainer.env
121if test -f $vault_env
122 if test -f $devcontainer_env
123 # Merge: update devcontainer.env with any keys from vault (vault wins)
124 for line in (cat $vault_env)
125 set -l key (string split -m1 '=' -- $line)[1]
126 if test -n "$key"
127 # Remove old key if exists, then append new
128 sed -i "/^$key=/d" $devcontainer_env 2>/dev/null
129 echo $line >> $devcontainer_env
130 end
131 end
132 log_ok "Synced secrets from vault to devcontainer.env"
133 else
134 # No devcontainer.env, just copy from vault
135 cp $vault_env $devcontainer_env
136 log_ok "Copied secrets from vault to devcontainer.env"
137 end
138else
139 log_warn "Vault env not found at $vault_env"
140end
141
142# --- Setup session-server SSH key from vault ---
143set -l ss_key_src ~/aesthetic-computer-vault/session-server/session_server
144set -l ss_key_dst ~/.ssh/session_server
145if test -f $ss_key_src; and not test -f $ss_key_dst
146 # Ensure .ssh directory exists with correct ownership and permissions
147 sudo mkdir -p ~/.ssh
148 sudo chown me:me ~/.ssh
149 sudo chmod 700 ~/.ssh
150
151 cp $ss_key_src $ss_key_dst
152 cp "$ss_key_src.pub" "$ss_key_dst.pub"
153 chmod 600 $ss_key_dst
154 chmod 644 "$ss_key_dst.pub"
155
156 # Create config file if it doesn't exist with correct ownership
157 if not test -f ~/.ssh/config
158 touch ~/.ssh/config
159 chmod 600 ~/.ssh/config
160 end
161
162 # Add SSH config entry if missing
163 if not grep -q "Host session-server" ~/.ssh/config 2>/dev/null
164 echo "" >> ~/.ssh/config
165 echo "Host session-server" >> ~/.ssh/config
166 echo " HostName 157.245.134.225" >> ~/.ssh/config
167 echo " User root" >> ~/.ssh/config
168 echo " IdentityFile ~/.ssh/session_server" >> ~/.ssh/config
169 echo " IdentitiesOnly yes" >> ~/.ssh/config
170 echo " StrictHostKeyChecking accept-new" >> ~/.ssh/config
171 end
172 log_ok "Session-server SSH key installed"
173else if not test -f $ss_key_src
174 log_warn "Session-server SSH key not found in vault"
175end
176
177log_step "PHASE 2: Checking for fast reload path"
178
179# Fast path for VS Code "Reload Window" - if .waiter exists and key processes running, skip setup
180if test -f /home/me/.waiter
181 log_info "Detected reload (found .waiter file)"
182
183 # Check if key processes are already running
184 set -l dockerd_running (pgrep -x dockerd 2>/dev/null)
185 set -l emacs_running (pgrep -f "emacs.*daemon" 2>/dev/null)
186
187 if test -n "$dockerd_running"
188 echo "✅ Docker daemon already running (PID: $dockerd_running)"
189
190 # Check if emacs daemon is running AND responsive
191 set -l emacs_config /home/me/aesthetic-computer/dotfiles/dot_config/emacs.el
192 if test -n "$emacs_running"
193 if emacsclient -e t >/dev/null 2>&1
194 echo "✅ Emacs daemon running and responsive (PID: $emacs_running)"
195 else
196 echo "⚠️ Emacs daemon zombie detected (PID: $emacs_running) - restarting..."
197 pkill -9 -f "emacs.*daemon" 2>/dev/null
198 pkill -9 emacs 2>/dev/null
199 sleep 1
200 emacs -q --daemon -l $emacs_config 2>&1
201 sleep 1
202 if emacsclient -e t >/dev/null 2>&1
203 echo "✅ Emacs daemon restarted successfully"
204 else
205 echo "❌ Failed to restart emacs daemon"
206 end
207 end
208 else
209 echo "⚠️ Emacs daemon not running - starting..."
210 emacs -q --daemon -l $emacs_config 2>&1
211 sleep 1
212 end
213
214 # Ensure CDP tunnel is up (for artery-tui to control VS Code on host)
215 set -l cdp_tunnel (pgrep -f "ssh.*9333.*host.docker.internal" 2>/dev/null)
216 if test -n "$cdp_tunnel"
217 echo "✅ CDP tunnel already running (PID: $cdp_tunnel)"
218 else
219 echo "🩸 Starting CDP tunnel..."
220 start_cdp_tunnel
221 if test $status -eq 0
222 echo "✅ CDP tunnel established (localhost:9333 → host:9333)"
223 else
224 echo "⚠️ CDP tunnel failed (artery may not control VS Code)"
225 end
226 end
227
228 echo "⚡ Skipping full setup - container already configured"
229 echo "✅ Ready! (fast reload path)"
230 exit 0
231 else
232 echo "⚠️ Docker daemon not found, running full setup..."
233 end
234end
235
236# Fix fish path if needed (Fedora installs to /usr/sbin instead of /usr/bin)
237if not test -f /usr/bin/fish; and test -f /usr/sbin/fish
238 sudo ln -sf /usr/sbin/fish /usr/bin/fish
239 echo "🐟 Created fish symlink: /usr/bin/fish -> /usr/sbin/fish"
240end
241
242# Function to ensure fish config directory has correct permissions
243function ensure_fish_config_permissions
244 set -l fish_config_dir /home/me/.config/fish
245 set -l fish_data_dir /home/me/.local/share/fish
246
247 echo "🔧 Fixing permissions for fish directories..."
248
249 # Fix the parent .config directory first (most important)
250 if test -d /home/me/.config
251 sudo chown -R me:me /home/me/.config 2>/dev/null
252 sudo chmod -R 755 /home/me/.config 2>/dev/null
253 echo "✅ Fixed permissions for /home/me/.config"
254 end
255
256 if test -d $fish_config_dir
257 # Fix ownership and permissions for fish config directory - be VERY aggressive
258 sudo chown -R me:me $fish_config_dir 2>/dev/null
259 sudo chmod -R 755 $fish_config_dir 2>/dev/null
260 sudo chmod 644 $fish_config_dir/*.fish 2>/dev/null
261 # Also fix individual subdirectories explicitly
262 sudo chown -R me:me $fish_config_dir/conf.d 2>/dev/null
263 sudo chown -R me:me $fish_config_dir/functions 2>/dev/null
264 sudo chown -R me:me $fish_config_dir/completions 2>/dev/null
265 echo "✅ Fixed permissions for $fish_config_dir"
266 end
267
268 if test -d $fish_data_dir
269 # Fix ownership and permissions for fish data directory
270 sudo chown -R me:me $fish_data_dir 2>/dev/null
271 sudo chmod -R 755 $fish_data_dir 2>/dev/null
272 echo "✅ Fixed permissions for $fish_data_dir"
273 end
274
275 # Fix /home/me/.local directory too
276 if test -d /home/me/.local
277 sudo chown -R me:me /home/me/.local 2>/dev/null
278 sudo chmod -R 755 /home/me/.local 2>/dev/null
279 echo "✅ Fixed permissions for /home/me/.local"
280 end
281
282 # Disable fish's universal variable file daemon (fishd) which causes permission issues
283 # We'll use fish_variables instead which is simpler and doesn't create temp files
284 set -U fish_greeting "" # Suppress greeting while we're at it
285
286 echo "✨ All permissions fixed!"
287end
288
289# Function to ensure correct Docker socket permissions
290function ensure_docker_socket_permissions
291 set -l DOCKER_SOCKET /var/run/docker.sock
292 if test -S $DOCKER_SOCKET
293 set -l DOCKER_GID (stat -c '%g' $DOCKER_SOCKET)
294 set -l CURRENT_DOCKER_GROUP_INFO (getent group $DOCKER_GID)
295
296 if test -z "$CURRENT_DOCKER_GROUP_INFO" # No group with this GID exists
297 if getent group docker > /dev/null
298 echo "Modifying existing docker group to GID $DOCKER_GID..."
299 sudo groupmod -g $DOCKER_GID docker
300 else
301 echo "Creating new docker group with GID $DOCKER_GID..."
302 sudo groupadd -g $DOCKER_GID docker
303 end
304 else # A group with this GID already exists, check if it's named 'docker'
305 set -l GROUP_NAME (echo $CURRENT_DOCKER_GROUP_INFO | cut -d: -f1)
306 if test "$GROUP_NAME" != "docker"
307 echo "Warning: Group with GID $DOCKER_GID exists but is named '$GROUP_NAME', not 'docker'. Manual intervention might be needed."
308 # Optionally, you could decide to add 'me' to this group anyway,
309 # or try to rename it if it's safe, or delete and recreate 'docker' group.
310 # For now, we'll proceed assuming 'docker' is the target group name.
311 if getent group docker > /dev/null
312 echo "Modifying existing docker group to GID $DOCKER_GID..."
313 sudo groupmod -g $DOCKER_GID docker
314 else
315 echo "Creating new docker group with GID $DOCKER_GID..."
316 sudo groupadd -g $DOCKER_GID docker
317 end
318 else
319 echo "Docker group '$GROUP_NAME' with GID $DOCKER_GID already exists."
320 end
321 end
322
323 # Ensure 'me' user is in the 'docker' group
324 if not groups me | string match -q -r '\\bdocker\\b'
325 echo "Adding user 'me' to docker group..."
326 sudo usermod -aG docker me
327 # Changes to group membership may require a new login session or `newgrp docker`
328 # to take effect immediately in the current shell.
329 # For a script, subsequent commands in this same script might not see the new group immediately.
330 # However, for processes spawned after this script, it should be fine.
331 else
332 echo "User 'me' is already in docker group."
333 end
334 else
335 echo "Docker socket $DOCKER_SOCKET not found."
336 end
337end
338
339function install_and_trust_certificate
340 set -l certificate_path $argv[1]
341 set -l certificate_key_path $argv[2]
342 set -l certificate_dir /etc/pki/ca-trust/source/anchors/
343
344 if test -z "$certificate_path"; or test -z "$certificate_key_path"
345 return
346 end
347
348 if test -f $certificate_path
349 sudo cp $certificate_path $certificate_dir
350 end
351
352 if test -f $certificate_key_path
353 sudo cp $certificate_key_path $certificate_dir
354 end
355
356 sudo update-ca-trust
357end
358
359function ensure_ssl_dev_certs
360 set -l ssl_dir /home/me/aesthetic-computer/ssl-dev
361 set -l nanos_ssl_dir /home/me/aesthetic-computer/nanos/ssl
362
363 if not test -d $ssl_dir
364 return
365 end
366
367 if not type -q mkcert
368 echo "⚠️ mkcert not available; skipping automatic certificate generation."
369 return
370 end
371
372 set -l previous_dir (pwd)
373 cd $ssl_dir
374
375 set -l have_cert 1
376 if not test -f localhost.pem; or not test -f localhost-key.pem
377 set have_cert 0
378 end
379
380 if test $have_cert -eq 0
381 echo "🔐 Generating mkcert certificates for the dev container..."
382 if not test -d /home/me/.local/share/mkcert
383 echo "📥 Installing mkcert local CA..."
384 log_cmd_timeout 60 mkcert -install
385 if test $status -ne 0
386 echo "⚠️ mkcert CA installation failed. See entry.fish log for details."
387 end
388 if not type -q certutil
389 echo "📦 Installing nss-tools for certificate trust..."
390 log_cmd_timeout 300 sudo dnf install -y nss-tools
391 log_cmd_timeout 60 mkcert -install
392 end
393 end
394
395 log_cmd_timeout 120 env nogreet=true fish ./ssl-install.fish
396 if test $status -ne 0
397 echo "⚠️ Automatic certificate generation failed using ssl-install.fish."
398 cd $previous_dir
399 return
400 end
401 else
402 log_cmd_timeout 120 env nogreet=true fish ./ssl-install.fish --install-only
403 end
404
405 mkdir -p $nanos_ssl_dir
406 cp localhost.pem $nanos_ssl_dir/localhost.pem
407 cp localhost-key.pem $nanos_ssl_dir/localhost-key.pem
408
409 sudo chown me:me -R $ssl_dir
410 sudo chown me:me -R $nanos_ssl_dir
411
412 install_and_trust_certificate $ssl_dir/localhost.pem $ssl_dir/localhost-key.pem
413
414 cd $previous_dir
415end
416
417log_step "PHASE 3: Docker socket permissions"
418# Call the function to set up Docker socket permissions
419ensure_docker_socket_permissions
420
421log_step "PHASE 4: Starting daemons"
422# Start Docker daemon in the background if not already running
423if not pgrep -x dockerd >/dev/null
424 log_info "Starting Docker daemon..."
425 sudo dockerd >/tmp/dockerd.log 2>&1 &
426else
427 log_ok "Docker daemon already running."
428end
429
430# Start Ollama daemon in the background if available and not already running
431if type -q ollama
432 if not pgrep -x ollama >/dev/null
433 log_info "Starting Ollama daemon..."
434 ollama serve >/tmp/ollama.log 2>&1 &
435 else
436 log_ok "Ollama daemon already running."
437 end
438else
439 log_warn "Ollama not found; skipping."
440end
441
442log_step "PHASE 4b: Cockpit web interface"
443# Start Cockpit web server (no TLS for local dev, port 9090)
444# Terminal + system overview work without systemd; service/journal features need systemd
445set -l cockpit_ws_bin ""
446for p in /usr/lib/cockpit/cockpit-ws /usr/libexec/cockpit-ws
447 if test -x $p
448 set cockpit_ws_bin $p
449 break
450 end
451end
452
453if test -n "$cockpit_ws_bin"
454 if not pgrep -f "cockpit-ws" >/dev/null
455 # Set a dev password so PAM auth works (use vault COCKPIT_PASSWORD if set)
456 set -l cockpit_pass (test -n "$COCKPIT_PASSWORD"; and echo $COCKPIT_PASSWORD; or echo "aesthetic")
457 echo "me:$cockpit_pass" | sudo chpasswd 2>/dev/null
458 sudo $cockpit_ws_bin --no-tls --port=9090 >/tmp/cockpit-ws.log 2>&1 &
459 disown
460 log_ok "Cockpit started → http://localhost:9090 (login: me / $cockpit_pass)"
461 else
462 log_ok "Cockpit already running"
463 end
464else
465 log_warn "Cockpit not installed — skipping (rebuild container to add it)"
466end
467
468log_step "PHASE 5: Environment setup"
469# Ensure the envs directory exists and is accessible (fallback if mount fails)
470if not test -d /home/me/envs
471 log_info "Creating missing /home/me/envs directory and linking to .devcontainer/envs"
472 mkdir -p /home/me/envs
473 ln -sf /workspaces/aesthetic-computer/.devcontainer/envs/* /home/me/envs/
474end
475
476if test -d /home/me/envs
477 source /home/me/envs/load_envs.fish
478 load_envs # Load devcontainer envs conditionally.
479 echo "🔧 Environment variables loaded from devcontainer.env"
480end
481
482# Set environment variables to prevent ETXTBSY errors and disable telemetry
483set -gx NETLIFY_CLI_TELEMETRY_DISABLED 1
484set -gx NODE_DISABLE_COMPILE_CACHE 1
485
486set -gx TERM xterm-256color
487
488# Create xdg-open wrapper to open URLs on Windows host from dev container
489if not test -f /usr/local/bin/xdg-open
490 echo "🌐 Creating xdg-open wrapper for host browser..."
491 printf '#!/bin/bash\n"\$BROWSER" "\$@"\n' | sudo tee /usr/local/bin/xdg-open >/dev/null
492 sudo chmod +x /usr/local/bin/xdg-open
493end
494
495# Start dbus-run-session for the keryring and execute the command passed to the script
496# dbus-run-session -- sh -c "eval \$(echo \$DBUS_SESSION_BUS_ADDRESS); exec $argv"
497
498# Send a welcome message!
499toilet "Aesthetic Computer" -f future | lolcat -x -r
500
501# Go to the user's directory.
502cd /home/me
503
504log_step "PHASE 6: SSL certificates"
505ensure_ssl_dev_certs
506
507log_step "PHASE 7: GitHub authentication"
508# Login to Github - use GH_TOKEN from vault if available
509set -l gh_authenticated 1
510if not gh auth status >/dev/null 2>&1
511 if test -n "$GH_TOKEN"
512 log_info "Authenticating to GitHub using GH_TOKEN..."
513 echo $GH_TOKEN | gh auth login --with-token
514 if gh auth status >/dev/null 2>&1
515 log_ok "GitHub authentication successful"
516 else
517 log_error "GitHub authentication failed"
518 log_warn "Continuing without GitHub auth; vault/git operations may be limited."
519 set gh_authenticated 0
520 end
521 else
522 log_warn "Not logged into GitHub and no GH_TOKEN available."
523 log_warn "Continuing startup without GitHub auth."
524 set gh_authenticated 0
525 end
526else
527 log_ok "Already authenticated to GitHub"
528end
529
530log_step "PHASE 8: Git configuration"
531if test -n "$GIT_USER_EMAIL"
532 git config --global user.email $GIT_USER_EMAIL
533 log_info "Set git user email to: $GIT_USER_EMAIL"
534end
535
536if test -n "$GIT_USER_NAME"
537 git config --global user.name $GIT_USER_NAME
538 log_info "Set git user name to: $GIT_USER_NAME"
539end
540
541# Add aesthetic-computer as the "safe" directory.
542git config --global --add safe.directory /home/me/aesthetic-computer
543
544# Set rebase as default for pull operations.
545git config --global pull.rebase true
546
547# Disable GPG signing to prevent commit issues in dev environment
548git config --global commit.gpgsign false
549
550# Make sure git is setup and authorized for making commits via `gh`.
551if test $gh_authenticated -eq 1
552 gh auth setup-git
553else
554 log_warn "Skipping 'gh auth setup-git' because GitHub auth is unavailable."
555end
556
557# Ensure GPG signing is disabled at both global and local levels (GitHub CLI might re-enable it)
558git config --global commit.gpgsign false
559cd /home/me/aesthetic-computer
560git config --local commit.gpgsign false
561git config --local user.signingkey ""
562echo "🔓 GPG signing disabled for commits"
563
564# Disable Netlify CLI telemetry to prevent ETXTBSY errors
565echo "Disabling Netlify CLI telemetry..."
566cd /home/me/aesthetic-computer/system && netlify --telemetry-disable 2>/dev/null || echo "Netlify telemetry disable completed"
567
568# Add aesthetic.local and sotce.local to /etc/hosts if they don't already exist
569if not grep -q "aesthetic.local" /etc/hosts
570 echo "127.0.0.1 aesthetic.local" | sudo tee -a /etc/hosts
571end
572
573if not grep -q "sotce.local" /etc/hosts
574 echo "127.0.0.1 sotce.local" | sudo tee -a /etc/hosts
575end
576
577log_step "PHASE 9: Vault setup"
578# Apply the 'vault' credentials to the mounted aesthetic-computer volume, and make sure it exists.
579if test -d /home/me/aesthetic-computer
580 if not test -d /home/me/aesthetic-computer/aesthetic-computer-vault
581 log_info "Cloning vault repository..."
582 gh repo clone whistlegraph/aesthetic-computer-vault /home/me/aesthetic-computer/aesthetic-computer-vault
583 cd /home/me/aesthetic-computer/aesthetic-computer-vault
584 log_info "Running devault.fish..."
585 sudo fish devault.fish
586
587 # Load environment variables after initial vault setup
588 if test -d /home/me/envs
589 source /home/me/envs/load_envs.fish
590 load_envs # Load envs after initial vault setup
591 log_ok "Environment variables loaded after initial vault setup"
592 end
593 else
594 cd /home/me/aesthetic-computer/aesthetic-computer-vault
595 log_info "Pulling latest vault..."
596 git pull
597 sudo fish devault.fish
598 log_ok "Vault mounted."
599 end
600
601 # Reload environment variables after vault is mounted
602 if test -d /home/me/envs
603 source /home/me/envs/load_envs.fish
604 load_envs # Reload envs after vault mount
605 log_ok "Environment variables reloaded after vault mount"
606 end
607
608 # Setup SSH keys from vault (only if not already set up)
609 set -l vault_ssh /home/me/aesthetic-computer/aesthetic-computer-vault/home/.ssh
610 if test -d $vault_ssh
611 # Only copy if id_rsa doesn't exist yet (first time setup)
612 if not test -f /home/me/.ssh/id_rsa
613 # Ensure .ssh directory exists with correct ownership and permissions
614 sudo mkdir -p /home/me/.ssh
615 sudo chown -R me:me /home/me/.ssh 2>/dev/null
616 sudo chmod 700 /home/me/.ssh 2>/dev/null
617
618 cp -f $vault_ssh/* /home/me/.ssh/ 2>/dev/null
619 chmod 700 /home/me/.ssh 2>/dev/null
620 chmod 600 /home/me/.ssh/id_rsa 2>/dev/null
621 chmod 644 /home/me/.ssh/id_rsa.pub 2>/dev/null
622 chmod 600 /home/me/.ssh/config 2>/dev/null
623 chmod 600 /home/me/.ssh/aesthetic_pds 2>/dev/null
624 chmod 644 /home/me/.ssh/aesthetic_pds.pub 2>/dev/null
625 log_ok "SSH keys restored from vault"
626 else
627 log_info "SSH keys already exist, skipping vault copy"
628 end
629 end
630
631 # Setup Copilot CLI config from vault (if volume is empty but vault has backup)
632 set -l vault_copilot /home/me/aesthetic-computer/aesthetic-computer-vault/home/.copilot
633 if test -d $vault_copilot
634 # Only restore if volume mount is empty (no config.json)
635 if not test -f /home/me/.copilot/config.json
636 mkdir -p /home/me/.copilot
637 cp -f $vault_copilot/config.json /home/me/.copilot/ 2>/dev/null
638 log_ok "Copilot CLI config restored from vault"
639 else
640 log_info "Copilot CLI config already exists (using volume data)"
641 end
642 end
643else
644 log_error "Vault unmounted! /home/me/aesthetic-computer not found"
645end
646
647log_step "PHASE 10: Fixing permissions"
648# Fix all permissions AFTER vault setup (vault copies files as root with sudo)
649log_info "Fixing permissions after vault setup..."
650
651# First, ensure the home directory itself is owned by me (critical for .emacs-logs etc)
652sudo chown me:me /home/me 2>/dev/null
653log_ok "Fixed ownership of /home/me"
654
655# Create .emacs-logs directory if it doesn't exist
656mkdir -p /home/me/.emacs-logs 2>/dev/null
657log_ok "Ensured /home/me/.emacs-logs exists"
658
659# Fix fish config permissions (vault may have overwritten with root-owned files)
660log_info "Fixing fish config permissions..."
661ensure_fish_config_permissions
662
663# Fix SSH directory permissions (use sudo to fix ownership if needed)
664if test -d /home/me/.ssh
665 if not test -O /home/me/.ssh
666 # SSH dir owned by root (probably from vault setup), fix ownership
667 sudo chown -R me:me /home/me/.ssh 2>/dev/null
668 log_ok "Fixed SSH directory ownership"
669 end
670 chmod 700 /home/me/.ssh 2>/dev/null
671 chmod 600 /home/me/.ssh/* 2>/dev/null
672 chmod 644 /home/me/.ssh/*.pub 2>/dev/null
673 log_ok "Fixed permissions for /home/me/.ssh"
674end
675
676# Fix Copilot CLI directory permissions (volume mount may have root ownership)
677if test -d /home/me/.copilot
678 sudo chown -R me:me /home/me/.copilot 2>/dev/null
679 sudo chmod 700 /home/me/.copilot 2>/dev/null
680 # Ensure pkg directory exists and is writable for package extraction
681 mkdir -p /home/me/.copilot/pkg 2>/dev/null
682 chmod 755 /home/me/.copilot/pkg 2>/dev/null
683 sudo chmod 600 /home/me/.copilot/config.json 2>/dev/null
684 echo "✅ Fixed permissions for /home/me/.copilot (Copilot CLI config)"
685end
686
687# Fix Claude/Codex config directory permissions after bind mounts are attached.
688# On Linux hosts, these mounts keep host ownership, so UID remapping is the main
689# fix; this container-side pass normalizes modes when ownership is already writable.
690for dir in /home/me/.claude /home/me/.codex
691 if test -d $dir
692 sudo chown -R me:me $dir 2>/dev/null
693 chmod 700 $dir 2>/dev/null
694 find $dir -type d -exec chmod 700 {} + 2>/dev/null
695 find $dir -type f -exec chmod 600 {} + 2>/dev/null
696 echo "✅ Fixed permissions for $dir"
697 end
698end
699
700if test -f /home/me/.claude.json
701 sudo chown me:me /home/me/.claude.json 2>/dev/null
702 chmod 600 /home/me/.claude.json 2>/dev/null
703 echo "✅ Fixed permissions for /home/me/.claude.json"
704end
705
706# Fix VS Code extension SecretStorage persistence paths.
707for dir in /home/me/.config/Code/User/globalStorage /home/me/.config/Code/User/workspaceStorage
708 if test -d $dir
709 sudo chown -R me:me $dir 2>/dev/null
710 find $dir -type d -exec chmod 700 {} + 2>/dev/null
711 find $dir -type f -exec chmod 600 {} + 2>/dev/null
712 echo "✅ Fixed permissions for $dir"
713 end
714end
715
716if test -d /home/me/.local/share/keyrings
717 sudo chown -R me:me /home/me/.local/share/keyrings 2>/dev/null
718 chmod 700 /home/me/.local/share/keyrings 2>/dev/null
719 find /home/me/.local/share/keyrings -type f -exec chmod 600 {} + 2>/dev/null
720 echo "✅ Fixed permissions for /home/me/.local/share/keyrings"
721end
722
723# --- GPG agent: cache passphrase for the entire container lifetime ---
724mkdir -p /home/me/.gnupg 2>/dev/null
725chmod 700 /home/me/.gnupg
726printf "allow-loopback-pinentry\npinentry-program /usr/sbin/pinentry-curses\ndefault-cache-ttl 999999\nmax-cache-ttl 999999\n" > /home/me/.gnupg/gpg-agent.conf
727gpgconf --reload gpg-agent 2>/dev/null
728log_ok "GPG agent configured (passphrase cached for container lifetime)"
729
730cd /home/me/aesthetic-computer/aesthetic-computer-vault
731git pull
732
733# Function to check and install npm dependencies in a directory
734set -g NODE_DEPS_CHECK_SCRIPT /workspaces/aesthetic-computer/.devcontainer/scripts/check-node-deps.mjs
735
736function verify_npm_versions
737 set -l dir $argv[1]
738 set -l dir_name (basename $dir)
739
740 if not test -f $NODE_DEPS_CHECK_SCRIPT
741 return
742 end
743
744 if not test -f $dir/package.json
745 return
746 end
747
748 if not test -d $dir/node_modules
749 return
750 end
751
752 log_cmd node $NODE_DEPS_CHECK_SCRIPT $dir
753 set -l check_status $status
754
755 if test $check_status -ne 0
756 echo "♻️ Resyncing dependencies in $dir_name to match package.json versions..."
757 cd $dir
758 log_cmd npm ci --no-fund --no-audit
759 if test $status -ne 0
760 echo "⚠️ npm ci failed in $dir_name, falling back to npm install"
761 log_cmd npm install --no-fund --no-audit
762 end
763 end
764end
765
766# Function to check and fix esbuild architecture mismatches
767function fix_esbuild_architecture
768 set -l dir $argv[1]
769 set -l dir_name (basename $dir)
770
771 # Check if this directory has esbuild installed
772 if test -d $dir/node_modules/@esbuild
773 set -l arch (uname -m)
774 set -l expected_esbuild
775
776 # Determine expected esbuild platform package
777 if test "$arch" = "x86_64"
778 set expected_esbuild "linux-x64"
779 else if test "$arch" = "aarch64"
780 set expected_esbuild "linux-arm64"
781 else
782 return 0 # Unknown architecture, skip
783 end
784
785 # Check what's actually installed
786 set -l installed_arch (ls $dir/node_modules/@esbuild 2>/dev/null | grep -E "^linux-")
787
788 if test -n "$installed_arch"; and test "$installed_arch" != "$expected_esbuild"
789 echo "🔧 Wrong esbuild architecture in $dir_name: $installed_arch (need $expected_esbuild)"
790 echo " Reinstalling dependencies to fix..."
791 cd $dir
792 rm -rf node_modules package-lock.json
793 log_cmd npm install --no-fund --no-audit
794 if test $status -eq 0
795 echo "✅ Fixed esbuild architecture in $dir_name"
796 else
797 echo "⚠️ Failed to fix esbuild in $dir_name"
798 end
799 end
800 end
801end
802
803function install_npm_deps
804 set -l dir $argv[1]
805 set -l dir_name (basename $dir)
806
807 if test -f $dir/package.json
808 if not test -d $dir/node_modules || not count $dir/node_modules/* >/dev/null 2>/dev/null
809 echo "📦 Installing dependencies in $dir_name..."
810 cd $dir
811 log_cmd npm ci --no-fund --no-audit
812 if test $status -ne 0
813 echo "⚠️ Failed to install dependencies in $dir_name, trying npm install..."
814 log_cmd npm install --no-fund --no-audit
815 end
816 else
817 echo "✅ $dir_name already has node_modules"
818 # Check for architecture mismatches even if node_modules exists
819 fix_esbuild_architecture $dir
820 end
821 verify_npm_versions $dir
822 end
823end
824
825log_step "PHASE 11: NPM dependencies"
826# Check and install dependencies in key directories
827cd /home/me/aesthetic-computer
828
829# Install root dependencies first
830if not test -d /home/me/aesthetic-computer/node_modules || not count /home/me/aesthetic-computer/node_modules/* >/dev/null
831 log_info "Installing root dependencies..."
832 log_cmd npm install -g npm@latest --no-fund --no-audit
833 log_cmd npm ci --no-fund --no-audit
834else
835 log_ok "Root directory already has node_modules"
836end
837
838log_info "Verifying npm versions..."
839verify_npm_versions /home/me/aesthetic-computer
840
841# Install dependencies in critical subdirectories (archive intentionally excluded)
842set -l critical_dirs system session-server vscode-extension nanos daemon utilities shared
843
844log_info "Installing dependencies in critical directories..."
845for dir in $critical_dirs
846 if test -d /home/me/aesthetic-computer/$dir
847 log_info "Processing $dir..."
848 install_npm_deps /home/me/aesthetic-computer/$dir
849 end
850end
851
852# Run the comprehensive install script for any remaining directories
853log_info "Running comprehensive install script for remaining directories..."
854pushd /home/me/aesthetic-computer
855log_cmd npm run install:everything-else
856popd
857
858log_step "PHASE 12: Final setup"
859# Make sure this user owns the emacs directory.
860sudo chown -R me:me ~/.emacs.d
861
862# Link the prompts directory to VSCode User config
863mkdir -p ~/.config/Code/User
864rm -rf ~/.config/Code/User/prompts
865ln -s /home/me/aesthetic-computer/prompts ~/.config/Code/User/prompts
866log_ok "Prompts directory linked to VSCode User config"
867
868# Link the modes directory to .github for VSCode chatmodes
869rm -rf /home/me/aesthetic-computer/.github
870ln -s /home/me/aesthetic-computer/modes /home/me/aesthetic-computer/.github
871log_ok "Modes directory linked to .github for VSCode chatmodes"
872
873# Source the devcontainer config to load AC development functions
874source /workspaces/aesthetic-computer/.devcontainer/config.fish
875log_ok "AC development functions loaded (ac-pack, ac-unpack, etc.)"
876
877# Copy feed secrets from vault to environment (if vault exists)
878if test -f /workspaces/aesthetic-computer/aesthetic-computer-vault/feed/.env
879 log_info "Loading feed system environment variables from vault..."
880 # Copy to devcontainer envs directory for persistence
881 cp /workspaces/aesthetic-computer/aesthetic-computer-vault/feed/.env /home/me/envs/feed.env
882 # Source it globally for the session
883 for line in (cat /workspaces/aesthetic-computer/aesthetic-computer-vault/feed/.env | grep -v '^#' | grep '=')
884 set -gx (string split '=' $line)
885 end
886 log_ok "Feed environment variables loaded"
887else
888 echo "⚠️ Feed vault not found - skipping feed environment setup"
889end
890
891# Set port 8888 to public in Codespaces (if applicable)
892if test -f /workspaces/aesthetic-computer/.devcontainer/scripts/set-port-public.fish
893 log_cmd fish /workspaces/aesthetic-computer/.devcontainer/scripts/set-port-public.fish
894end
895
896# Create .waiter file to signal container is ready (moved from postAttachCommand to avoid terminal popup in Codespaces)
897sudo touch /home/me/.waiter
898sudo chmod 777 /home/me/.waiter
899sudo chmod +w /home/me
900echo "✅ Container ready signal created (.waiter)"
901
902# NOTE: Emacs daemon is NOT pre-started here. The `aesthetic` function
903# (called by the VS Code "💻 Aesthetic" task) unconditionally kills any
904# running daemon and starts fresh via `ensure-emacs-daemon-ready`.
905# Pre-starting here caused a race condition: entry.fish would start a
906# daemon, then the VS Code task would immediately kill it and start
907# another, wasting resources and causing load spikes that made the
908# second daemon unresponsive to the crash monitor.
909log_step "PHASE 12b: Emacs daemon (deferred to VS Code task)"
910log_info "Emacs daemon start deferred - will be started by 'aesthetic' function"
911
912# 🩸 Setup SSH tunnel for CDP (Chrome DevTools Protocol) to host
913# This allows artery-tui to control VS Code on the host machine
914if test -n "$HOST_IP"; or test (uname -s) != "Linux"
915 # Kill any existing CDP tunnels
916 pkill -f "ssh.*9333.*host.docker.internal" 2>/dev/null
917
918 # Create tunnel in background (port 9333 to avoid conflicts with svchost.exe on 9222)
919 start_cdp_tunnel
920 if test $status -eq 0
921 echo "🩸 CDP tunnel established (localhost:9333 → host:9333)"
922 else
923 echo "⚠️ CDP tunnel failed (artery may not work)"
924 end
925end
926
927# 🧊 Start TURN server for WebRTC relay (required for UDP in Docker)
928if which turnserver >/dev/null 2>&1
929 pkill -f "turnserver" 2>/dev/null
930 sleep 0.5
931 # Get host LAN IP from vault/machines.json or use default
932 set -l HOST_LAN_IP "192.168.1.127"
933 set -l HOST_LAN_IP_SOURCE "default"
934 if test -f /workspaces/aesthetic-computer/aesthetic-computer-vault/machines.json
935 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)
936 if test -n "$detected_ip"
937 set HOST_LAN_IP $detected_ip
938 set HOST_LAN_IP_SOURCE "vault"
939 end
940 end
941 log_info "TURN host IP: $HOST_LAN_IP (source: $HOST_LAN_IP_SOURCE)"
942 echo $HOST_LAN_IP > /tmp/host-lan-ip
943 turnserver -c /workspaces/aesthetic-computer/.devcontainer/turnserver.conf --external-ip=$HOST_LAN_IP >> $ENTRY_LOG_REAL 2>&1 &
944 disown
945 log_ok "TURN server started (localhost:3478, external-ip: $HOST_LAN_IP)"
946else
947 log_warn "coturn not installed, UDP may not work across networks"
948end
949
950# echo "Initializing 📋 Clipboard Service" | lolcat -x -r
951
952# ❤️🔥
953# TODO: This may not be the best method because cdocker aesthetic needs to be
954# running in the user environment that has the clipboard.
955
956# Run the isomorphic_copy clipboard on the host.
957# Make sure the host allows ssh access from its own private key... `ssh-copy-id -i ~/.ssh/id_rsa.pub $USER@localhost`
958# ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $HOST_USER@172.17.0.1
959
960# ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $HOST_USER@172.17.0.1 "cdocker aesthetic"
961
962# 🔍 Ensure Devcontainer Status Server is still running (it was started early)
963log_info "Checking Devcontainer Status Server..."
964if pgrep -f "devcontainer-status" >/dev/null
965 log_ok "Status Server still running on http://127.0.0.1:7890"
966else if test -f /workspaces/aesthetic-computer/artery/devcontainer-status.mjs
967 log_warn "Status Server died, restarting..."
968 nohup node /workspaces/aesthetic-computer/artery/devcontainer-status.mjs --server >/tmp/devcontainer-status.log 2>&1 &
969 disown
970 log_ok "Status Server restarted on http://127.0.0.1:7890"
971end
972
973log "════════════════════════════════════════════════════════════════"
974log "🎉 ENTRY.FISH COMPLETED SUCCESSFULLY"
975log "════════════════════════════════════════════════════════════════"
976log_info "End time: "(date -Iseconds)
977log_info "Log file: $ENTRY_LOG_REAL"
978
979# Auto-launch is handled by the VS Code task (💻 Aesthetic) which runs on folder open
980# The task calls aesthetic-launch.sh which properly launches the emacs client
981# Background launch from entry.fish doesn't work because emacsclient -nw needs an interactive terminal
982log_info "Container setup complete - VS Code task will launch aesthetic platform"