Issue #12 - MQTT_PASSWORD was copied into every job record, which any sub-agent can read. BrokerConfig.to_registry_block no longer persists it; broker_config_from_job layers the password back from the environment (.mam.env) at connect time, so publish_event still connects from the record. _atomic_write_record scrubs broker.password from any record it writes, and `registry.py scrub-secrets [--dry-run]` migrates an existing registry. auth_token/dispatch_token stay: the agent's own publish_event needs them. Issue #8 - update.sh restored the pre-update asset_hashes.txt and version.txt over the installer's fresh ones, so version.txt named the previous commit and remove.sh backed up every pristine updated file as "locally modified". The success path now keeps the installer's stamps and only merges install_state keys the re-install did not write (gitignore_created); the failure path still restores everything verbatim. Issue #7 - the shim's new-session took the first same-cwd pane, always found the full first workspace, judged it overflow and opened another workspace per agent (4 + 1 + 1). It now walks every same-cwd workspace in pane order and takes the first with an idle pane or room in its 2xK grid; a workspace is created only when all are full (4 + 2). Tests: test_issue7_workspace_packing.py (3; the mock herdr gains an opt-in per-workspace 2x2 geometry), test_issue8_update_state.py (1, real install+update cycle), test_issue12_registry_secrets.py (4); registry CLI snapshot and help capture refreshed for the intentional scrub-secrets subcommand; registry.md documents the broker block. Refs #12 #8 #7 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
260 lines
9.2 KiB
Bash
260 lines
9.2 KiB
Bash
#!/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
|
|
# Issue #8: asset_hashes.txt and version.txt describe the tree the
|
|
# installer just wrote, so the pre-update copies must NOT come back here -
|
|
# restoring them made version.txt name the previous commit and remove.sh
|
|
# treat every updated file as locally modified (spurious backups). Only
|
|
# install_state carries history worth keeping: merge in any key the fresh
|
|
# install did not write (e.g. gitignore_created from the original install).
|
|
# The failure path (restore_on_failure) still puts all three back verbatim.
|
|
if [ -f ".mam.update-tmp/install_state" ]; then
|
|
touch .mam/install_state
|
|
while IFS= read -r line || [ -n "$line" ]; do
|
|
key="${line%%=*}"
|
|
[ -n "$key" ] || continue
|
|
if ! grep -q "^${key}=" .mam/install_state 2>/dev/null; then
|
|
printf '%s\n' "$line" >> .mam/install_state
|
|
fi
|
|
done < .mam.update-tmp/install_state
|
|
fi
|
|
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 "===================================================================="
|