From 8b4b80b4d0cd580a57fa97ca5cfddf5fab80f63f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 26 Mar 2026 08:40:36 +0000 Subject: [PATCH 1/3] fix: resolve DMG creation failure caused by Electron hardlinks create-dmg uses `du` to estimate app size, but Electron Framework's hardlinks cause `du` to undercount. hdiutil copies each hardlinked file independently, exhausting DMG space and failing with ENOSPC (misreported as "resource busy"). Fix by staging the app with rsync --no-hard-links to break hardlinks before passing to create-dmg. https://claude.ai/code/session_01S1crsR6m3fZEHYGpCuDiEJ --- libs/hexagent_demo/electron/scripts/create-dmg.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libs/hexagent_demo/electron/scripts/create-dmg.sh b/libs/hexagent_demo/electron/scripts/create-dmg.sh index 96f96592..f30c7b8c 100755 --- a/libs/hexagent_demo/electron/scripts/create-dmg.sh +++ b/libs/hexagent_demo/electron/scripts/create-dmg.sh @@ -35,6 +35,15 @@ rm -f "$DMG_PATH" echo "Creating DMG: $(basename "$DMG_PATH")" +# Electron Framework uses hardlinks extensively. `du` counts hardlinked inodes +# once, causing create-dmg to underestimate the DMG size needed and fail with +# "No space left on device". Copy the app first to break hardlinks so that +# the size estimate matches the actual bytes written. +STAGING_DIR=$(mktemp -d) +APP_PATH_COPY="$STAGING_DIR/${PRODUCT_NAME}.app" +echo "Staging app (resolving hardlinks)..." +rsync -a --no-hard-links "$APP_PATH/" "$APP_PATH_COPY/" + create-dmg \ --volname "$PRODUCT_NAME" \ --background "$BACKGROUND" \ @@ -44,6 +53,8 @@ create-dmg \ --app-drop-link 415 185 \ --no-internet-enable \ "$DMG_PATH" \ - "$APP_PATH" + "$APP_PATH_COPY" + +rm -rf "$STAGING_DIR" echo "DMG created: $DMG_PATH" From eaf6095801e2a867532cb5b8b9b5219f158be167 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 26 Mar 2026 09:14:38 +0000 Subject: [PATCH 2/3] fix: replace rsync with ditto to break Electron hardlinks on macOS macOS ships with BSD rsync 2.6.9 which does not support --no-hard-links. Switch to `ditto`, a macOS-native tool that copies .app bundles correctly (preserving xattrs, symlinks, resource forks) without preserving hardlinks, ensuring create-dmg gets an accurate size estimate. https://claude.ai/code/session_01S1crsR6m3fZEHYGpCuDiEJ --- libs/hexagent_demo/electron/scripts/create-dmg.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/hexagent_demo/electron/scripts/create-dmg.sh b/libs/hexagent_demo/electron/scripts/create-dmg.sh index f30c7b8c..7dfa41a9 100755 --- a/libs/hexagent_demo/electron/scripts/create-dmg.sh +++ b/libs/hexagent_demo/electron/scripts/create-dmg.sh @@ -42,7 +42,7 @@ echo "Creating DMG: $(basename "$DMG_PATH")" STAGING_DIR=$(mktemp -d) APP_PATH_COPY="$STAGING_DIR/${PRODUCT_NAME}.app" echo "Staging app (resolving hardlinks)..." -rsync -a --no-hard-links "$APP_PATH/" "$APP_PATH_COPY/" +ditto "$APP_PATH" "$APP_PATH_COPY" create-dmg \ --volname "$PRODUCT_NAME" \ From ae0cadb3c2877bde341d13fe58efb7916fc29278 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 27 Mar 2026 06:17:46 +0000 Subject: [PATCH 3/3] feat(electron): add macOS uninstall script Removes HexAgent.app, all app data/caches/prefs, and the hexagent Lima VM instance (stops if running, then deletes via limactl). Supports --force flag to skip the confirmation prompt. https://claude.ai/code/session_01S1crsR6m3fZEHYGpCuDiEJ --- .../electron/scripts/uninstall-mac.sh | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100755 libs/hexagent_demo/electron/scripts/uninstall-mac.sh diff --git a/libs/hexagent_demo/electron/scripts/uninstall-mac.sh b/libs/hexagent_demo/electron/scripts/uninstall-mac.sh new file mode 100755 index 00000000..ca00149d --- /dev/null +++ b/libs/hexagent_demo/electron/scripts/uninstall-mac.sh @@ -0,0 +1,120 @@ +#!/usr/bin/env bash +# Uninstall HexAgent from macOS. +# +# Removes: +# - HexAgent.app from /Applications +# - All app data, caches, logs, preferences (com.hexagent.app) +# - The "hexagent" Lima VM instance and its disk image +# +# Usage: +# bash uninstall-mac.sh # interactive (asks for confirmation) +# bash uninstall-mac.sh --force # skip confirmation prompt +set -euo pipefail + +APP_NAME="HexAgent" +APP_ID="com.hexagent.app" +LIMA_INSTANCE="hexagent" +FORCE="${1:-}" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +echo "" +echo "========================================" +echo " $APP_NAME Uninstaller" +echo "========================================" +echo "" +echo "This will permanently delete:" +echo " • /Applications/${APP_NAME}.app" +echo " • ~/Library/Application Support/${APP_NAME}" +echo " • ~/Library/Caches/${APP_ID}" +echo " • ~/Library/Logs/${APP_NAME}" +echo " • ~/Library/Preferences/${APP_ID}.plist" +echo " • ~/Library/Saved Application State/${APP_ID}.savedState" +echo " • Lima VM instance: ${LIMA_INSTANCE} (~/.lima/${LIMA_INSTANCE})" +echo "" + +if [ "$FORCE" != "--force" ]; then + read -r -p "Continue? [y/N] " confirm + case "$confirm" in + [yY][eE][sS]|[yY]) ;; + *) + echo "Cancelled." + exit 0 + ;; + esac +fi + +echo "" + +# ── 1. Quit the app if running ───────────────────────────────────────────── +if pgrep -x "$APP_NAME" &>/dev/null; then + echo -n "Quitting ${APP_NAME}... " + pkill -x "$APP_NAME" || true + sleep 1 + echo -e "${GREEN}done${NC}" +fi + +# ── 2. Stop and delete the Lima VM ──────────────────────────────────────── +if command -v limactl &>/dev/null; then + if limactl list --format '{{.Name}}' 2>/dev/null | grep -qx "$LIMA_INSTANCE"; then + STATUS=$(limactl list --format '{{.Name}} {{.Status}}' 2>/dev/null \ + | awk -v name="$LIMA_INSTANCE" '$1==name{print $2}') + if [ "$STATUS" = "Running" ]; then + echo -n "Stopping Lima VM '${LIMA_INSTANCE}'... " + limactl stop "$LIMA_INSTANCE" 2>/dev/null || true + echo -e "${GREEN}done${NC}" + fi + echo -n "Deleting Lima VM '${LIMA_INSTANCE}'... " + limactl delete "$LIMA_INSTANCE" 2>/dev/null || true + echo -e "${GREEN}done${NC}" + else + echo -e "${YELLOW}Lima VM '${LIMA_INSTANCE}' not found — skipping.${NC}" + fi +else + echo -e "${YELLOW}limactl not installed — skipping Lima VM removal.${NC}" +fi + +# Fallback: remove the Lima data directory directly if limactl left it behind +if [ -d "$HOME/.lima/$LIMA_INSTANCE" ]; then + echo -n "Removing ~/.lima/${LIMA_INSTANCE}... " + rm -rf "$HOME/.lima/$LIMA_INSTANCE" + echo -e "${GREEN}done${NC}" +fi + +# ── 3. Remove the .app bundle ───────────────────────────────────────────── +if [ -d "/Applications/${APP_NAME}.app" ]; then + echo -n "Removing /Applications/${APP_NAME}.app... " + rm -rf "/Applications/${APP_NAME}.app" + echo -e "${GREEN}done${NC}" +else + echo -e "${YELLOW}/Applications/${APP_NAME}.app not found — skipping.${NC}" +fi + +# ── 4. Remove app data & caches ─────────────────────────────────────────── +declare -a PATHS=( + "$HOME/Library/Application Support/${APP_NAME}" + "$HOME/Library/Caches/${APP_ID}" + "$HOME/Library/Caches/${APP_NAME}" + "$HOME/Library/Logs/${APP_NAME}" + "$HOME/Library/Preferences/${APP_ID}.plist" + "$HOME/Library/Saved Application State/${APP_ID}.savedState" + "$HOME/Library/WebKit/${APP_ID}" + "$HOME/Library/HTTPStorages/${APP_ID}" +) + +for p in "${PATHS[@]}"; do + if [ -e "$p" ]; then + echo -n "Removing ${p/$HOME/~}... " + rm -rf "$p" + echo -e "${GREEN}done${NC}" + fi +done + +echo "" +echo -e "${GREEN}========================================" +echo " HexAgent uninstalled successfully." +echo -e "========================================${NC}" +echo ""