#!/usr/bin/env bash # ============================================================================== # update.sh — Multi-Agent Mux (MAM) Orchestration Updater # ============================================================================== # Safely updates MAM skills, virtual environment, and docs to the latest version. # Preserves user configuration (.env) and local metadata/jobs database (.mam). # ============================================================================== set -euo pipefail TARGET_DIR="" FORCE=0 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Parse arguments while [[ $# -gt 0 ]]; do case "$1" in -y|--yes|--force) FORCE=1 shift ;; *) TARGET_DIR="$1" shift ;; esac done if [ -z "$TARGET_DIR" ]; then if [ "$(basename "$SCRIPT_DIR")" = ".mam_deploy" ]; then TARGET_DIR="$(dirname "$SCRIPT_DIR")" else TARGET_DIR="$(pwd)" fi else if [ "$(basename "$TARGET_DIR")" = ".mam_deploy" ]; then TARGET_DIR="$(dirname "$TARGET_DIR")" fi if [ ! -d "$TARGET_DIR" ]; then echo "❌ Error: Target directory '$TARGET_DIR' does not exist." >&2 exit 1 fi TARGET_DIR="$(cd "$TARGET_DIR" && pwd)" fi echo "====================================================================" echo "⚡ Starting Multi-Agent Mux (MAM) Update" echo "📂 Target Workspace: $TARGET_DIR" echo "====================================================================" cd "$TARGET_DIR" # 1. Verification of existing install (B-2: dual resolution) REMOVER="" for cand in ".mam_deploy/remove.sh" "remove.sh"; do if [ -f "$cand" ]; then REMOVER="$cand" break fi done if [ -z "$REMOVER" ]; then echo "❌ Error: No MAM installation (remove.sh) found in '$TARGET_DIR'." >&2 echo " Please run install.sh first to set up the workspace." >&2 exit 1 fi # Request confirmation if not forced if [ $FORCE -eq 0 ]; then echo "⚠️ WARNING: This will update MAM orchestration skills, virtual environment, " echo " and docs to the latest version." echo " (Your configuration, job history, and custom skills will be preserved)." if [ ! -t 0 ]; then echo "❌ Error: Non-interactive terminal detected. Please run with -y/--yes/--force." >&2 exit 1 fi if ! read -p "❓ Proceed with update? [y/N]: " -r response; then response="n" fi if [[ ! "$response" =~ ^[yY](es)?$ ]]; then echo "❌ Update cancelled by user." exit 0 fi fi # 1.5 Pre-capture legacy ownership before remove.sh deletes the manifest (M-4) MAM_LEGACY_ENV_OWNED=0 if [ -f ".mam/install_manifest.txt" ] && grep -Fqx ".env" ".mam/install_manifest.txt" 2>/dev/null; then MAM_LEGACY_ENV_OWNED=1 fi export MAM_LEGACY_ENV_OWNED # 2. Stage backups of user configurations and metadata to prevent deletion echo "💾 Backing up configuration and database..." HAS_ENV=0 ENV_BACKUP_SRC="" ENV_BACKUP_TMP="" if [ -f ".mam.env" ]; then HAS_ENV=1 ENV_BACKUP_SRC=".mam.env" ENV_BACKUP_TMP=".mam.env.update-tmp" mv ".mam.env" "$ENV_BACKUP_TMP" elif [ -f ".env" ]; then HAS_ENV=1 ENV_BACKUP_SRC=".env" ENV_BACKUP_TMP=".env.update-tmp" mv ".env" "$ENV_BACKUP_TMP" fi HAS_MAM=0 if [ -d ".mam" ]; then HAS_MAM=1 # Copy database and jobs to temporary backup outside of .mam. # We do NOT move the .mam folder away so that remove.sh can still read .mam/install_manifest.txt! mkdir -p .mam.update-tmp # Copy SQLite databases and session files for db in .mam/agent-sessions.*; do if [ -f "$db" ]; then cp -f "$db" .mam.update-tmp/ fi done # Copy jobs history if [ -d ".mam/jobs" ] && [ "$(ls -A .mam/jobs 2>/dev/null)" ]; then mkdir -p .mam.update-tmp/jobs cp -rf .mam/jobs/* .mam.update-tmp/jobs/ fi # Copy delegate logs if [ -d ".mam/delegate_job_logs" ] && [ "$(ls -A .mam/delegate_job_logs 2>/dev/null)" ]; then mkdir -p .mam.update-tmp/delegate_job_logs cp -rf .mam/delegate_job_logs/* .mam.update-tmp/delegate_job_logs/ fi # Copy manifest so we have a backup if [ -f ".mam/install_manifest.txt" ]; then cp -f .mam/install_manifest.txt .mam.update-tmp/ fi # C12: Copy MAM state files across update cycle for st in install_state asset_hashes.txt version.txt; do if [ -f ".mam/$st" ]; then cp -f ".mam/$st" .mam.update-tmp/ fi done if [ -d ".mam/skill-backups" ]; then mkdir -p .mam.update-tmp/skill-backups cp -rf .mam/skill-backups/* .mam.update-tmp/skill-backups/ 2>/dev/null || true fi fi # Define trap to restore backup files on failure restore_on_failure() { echo "❌ Update failed. Reverting configuration and database to previous state..." if [ $HAS_ENV -eq 1 ] && [ -n "$ENV_BACKUP_TMP" ] && [ -f "$ENV_BACKUP_TMP" ]; then mv -f "$ENV_BACKUP_TMP" "$ENV_BACKUP_SRC" 2>/dev/null || true fi if [ $HAS_MAM -eq 1 ] && [ -d ".mam.update-tmp" ]; then # Revert to old database/jobs backup by restoring .mam directory rm -rf .mam 2>/dev/null || true mkdir -p .mam cp -f .mam.update-tmp/agent-sessions.* .mam/ 2>/dev/null || true if [ -d ".mam.update-tmp/jobs" ]; then cp -rf .mam.update-tmp/jobs .mam/ 2>/dev/null || true fi if [ -d ".mam.update-tmp/delegate_job_logs" ]; then cp -rf .mam.update-tmp/delegate_job_logs .mam/ 2>/dev/null || true fi if [ -f ".mam.update-tmp/install_manifest.txt" ]; then cp -f .mam.update-tmp/install_manifest.txt .mam/ 2>/dev/null || true fi for st in install_state asset_hashes.txt version.txt; do if [ -f ".mam.update-tmp/$st" ]; then cp -f ".mam.update-tmp/$st" .mam/ 2>/dev/null || true fi done if [ -d ".mam.update-tmp/skill-backups" ]; then mkdir -p .mam/skill-backups cp -rf .mam.update-tmp/skill-backups/* .mam/skill-backups/ 2>/dev/null || true fi rm -rf .mam.update-tmp 2>/dev/null || true fi } trap restore_on_failure EXIT # 3. Perform uninstallation of existing files echo "🗑️ Removing existing installation..." # remove.sh will run in manifest mode because .mam/install_manifest.txt is still present. # It will delete .agents/, documents, scripts, .venv, and .mam folder. bash "$REMOVER" --force "$TARGET_DIR" # 4. Fetch and run the latest installer from Gitea echo "📥 Fetching and running the latest installer..." # Note: MAM_*_URL environment variables are automatically inherited by the child installer bash process. INSTALLER_URL="${MAM_INSTALLER_URL:-https://git.godopu.com/tmpl/multi-agent-mux/raw/branch/main/deploy/install.sh}" if command -v curl &>/dev/null; then curl -fsSL "$INSTALLER_URL" | bash -s -- "$TARGET_DIR" elif command -v wget &>/dev/null; then wget -qO- "$INSTALLER_URL" | bash -s -- "$TARGET_DIR" else echo "❌ Error: Neither 'curl' nor 'wget' is available to fetch the installer." >&2 exit 1 fi # Disable failure trap since installation succeeded trap - EXIT # 5. Restore backups of configuration and database echo "🔄 Restoring configuration and database..." if [ $HAS_ENV -eq 1 ] && [ -n "$ENV_BACKUP_TMP" ] && [ -f "$ENV_BACKUP_TMP" ]; then if [ "$MAM_LEGACY_ENV_OWNED" = "1" ] && [ "$ENV_BACKUP_SRC" = ".env" ] && [ ! -f ".mam.env" ]; then mv -f "$ENV_BACKUP_TMP" ".mam.env" chmod 0600 ".mam.env" 2>/dev/null || true else mv -f "$ENV_BACKUP_TMP" "$ENV_BACKUP_SRC" fi fi if [ $HAS_MAM -eq 1 ]; then if [ -d ".mam.update-tmp" ]; then # The installer created a new .mam directory with a new manifest. # We want to merge the old .mam database/jobs back while keeping the new manifest. for db in .mam.update-tmp/agent-sessions.*; do if [ -f "$db" ]; then cp -f "$db" .mam/ fi done if [ -d ".mam.update-tmp/jobs" ] && [ "$(ls -A .mam.update-tmp/jobs 2>/dev/null)" ]; then mkdir -p .mam/jobs cp -rf .mam.update-tmp/jobs/* .mam/jobs/ fi if [ -d ".mam.update-tmp/delegate_job_logs" ] && [ "$(ls -A .mam.update-tmp/delegate_job_logs 2>/dev/null)" ]; then mkdir -p .mam/delegate_job_logs cp -rf .mam.update-tmp/delegate_job_logs/* .mam/delegate_job_logs/ fi for st in install_state asset_hashes.txt version.txt; do if [ -f ".mam.update-tmp/$st" ]; then cp -f ".mam.update-tmp/$st" .mam/ fi done if [ -d ".mam.update-tmp/skill-backups" ]; then mkdir -p .mam/skill-backups cp -rf .mam.update-tmp/skill-backups/* .mam/skill-backups/ 2>/dev/null || true fi rm -rf ".mam.update-tmp" fi fi echo "====================================================================" echo "🎉 Update complete!" echo "===================================================================="