#!/usr/bin/env bash set -euo pipefail # Build and bundle Clawdis into a minimal .app we can open. # Outputs to dist/Clawdis.app ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" APP_ROOT="$ROOT_DIR/dist/Clawdis.app" BUILD_PATH="$ROOT_DIR/apps/macos/.build" PRODUCT="Clawdis" BUNDLE_ID="${BUNDLE_ID:-com.steipete.clawdis.debug}" PKG_VERSION="$(cd "$ROOT_DIR" && node -p "require('./package.json').version" 2>/dev/null || echo "0.0.0")" BUILD_TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ") GIT_COMMIT=$(cd "$ROOT_DIR" && git rev-parse --short HEAD 2>/dev/null || echo "unknown") APP_VERSION="${APP_VERSION:-$PKG_VERSION}" APP_BUILD="${APP_BUILD:-$PKG_VERSION}" BUILD_CONFIG="${BUILD_CONFIG:-debug}" SPARKLE_PUBLIC_ED_KEY="${SPARKLE_PUBLIC_ED_KEY:-AGCY8w5vHirVfGGDGc8Szc5iuOqupZSh9pMj/Qs67XI=}" SPARKLE_FEED_URL="${SPARKLE_FEED_URL:-https://raw.githubusercontent.com/steipete/clawdis/main/appcast.xml}" AUTO_CHECKS=true if [[ "$BUNDLE_ID" == *.debug ]]; then SPARKLE_FEED_URL="" AUTO_CHECKS=false fi echo "๐Ÿ“ฆ Ensuring deps (pnpm install)" (cd "$ROOT_DIR" && pnpm install --no-frozen-lockfile --config.node-linker=hoisted) if [[ "${SKIP_TSC:-0}" != "1" ]]; then echo "๐Ÿ“ฆ Building JS (pnpm exec tsc)" (cd "$ROOT_DIR" && pnpm exec tsc -p tsconfig.json) else echo "๐Ÿ“ฆ Skipping TS build (SKIP_TSC=1)" fi cd "$ROOT_DIR/apps/macos" echo "๐Ÿ”จ Building $PRODUCT ($BUILD_CONFIG)" swift build -c "$BUILD_CONFIG" --product "$PRODUCT" --product "${PRODUCT}CLI" --build-path "$BUILD_PATH" BIN="$BUILD_PATH/$BUILD_CONFIG/$PRODUCT" CLI_BIN="$BUILD_PATH/$BUILD_CONFIG/ClawdisCLI" echo "pkg: binary $BIN" >&2 echo "pkg: cli $CLI_BIN" >&2 echo "๐Ÿงน Cleaning old app bundle" rm -rf "$APP_ROOT" mkdir -p "$APP_ROOT/Contents/MacOS" mkdir -p "$APP_ROOT/Contents/Resources" mkdir -p "$APP_ROOT/Contents/Resources/Relay" mkdir -p "$APP_ROOT/Contents/Frameworks" echo "๐Ÿ“„ Writing Info.plist" cat > "$APP_ROOT/Contents/Info.plist" < CFBundleIdentifier ${BUNDLE_ID} CFBundleShortVersionString ${APP_VERSION} CFBundleVersion ${APP_BUILD} CFBundleName Clawdis CFBundleExecutable Clawdis CFBundleIconFile Clawdis CFBundlePackageType APPL LSMinimumSystemVersion 15.0 LSUIElement ClawdisBuildTimestamp ${BUILD_TS} ClawdisGitCommit ${GIT_COMMIT} SUFeedURL ${SPARKLE_FEED_URL} SUPublicEDKey ${SPARKLE_PUBLIC_ED_KEY} SUEnableAutomaticChecks <${AUTO_CHECKS}/> NSUserNotificationUsageDescription Clawdis needs notification permission to show alerts for agent actions. NSScreenCaptureDescription Clawdis captures the screen when the agent needs screenshots for context. NSMicrophoneUsageDescription Clawdis needs the mic for Voice Wake tests and agent audio capture. NSSpeechRecognitionUsageDescription Clawdis uses speech recognition to detect your Voice Wake trigger phrase. NSAppleEventsUsageDescription Clawdis needs Automation (AppleScript) permission to drive Terminal and other apps for agent actions. PLIST echo "๐Ÿšš Copying binary" cp "$BIN" "$APP_ROOT/Contents/MacOS/Clawdis" chmod +x "$APP_ROOT/Contents/MacOS/Clawdis" SPARKLE_FRAMEWORK="$BUILD_PATH/$BUILD_CONFIG/Sparkle.framework" if [ -d "$SPARKLE_FRAMEWORK" ]; then echo "โœจ Embedding Sparkle.framework" cp -R "$SPARKLE_FRAMEWORK" "$APP_ROOT/Contents/Frameworks/" chmod -R a+rX "$APP_ROOT/Contents/Frameworks/Sparkle.framework" install_name_tool -add_rpath "@executable_path/../Frameworks" "$APP_ROOT/Contents/MacOS/Clawdis" fi echo "๐Ÿ–ผ Copying app icon" cp "$ROOT_DIR/apps/macos/Sources/Clawdis/Resources/Clawdis.icns" "$APP_ROOT/Contents/Resources/Clawdis.icns" echo "๐Ÿ“ฆ Copying WebChat resources" rsync -a "$ROOT_DIR/apps/macos/Sources/Clawdis/Resources/WebChat" "$APP_ROOT/Contents/Resources/" echo "๐Ÿงน Removing unused pdfjs legacy bundle" rm -rf "$APP_ROOT/Contents/Resources/WebChat/vendor/pdfjs-dist/legacy" RELAY_DIR="$APP_ROOT/Contents/Resources/Relay" if [[ "${SKIP_RELAY_PACKAGE:-0}" != "1" ]]; then echo "๐Ÿงฐ Staging relay payload (dist + node_modules; expects system Node โ‰ฅ22)" rsync -a --delete --exclude "Clawdis.app" "$ROOT_DIR/dist/" "$RELAY_DIR/dist/" cp "$ROOT_DIR/package.json" "$RELAY_DIR/" cp "$ROOT_DIR/pnpm-lock.yaml" "$RELAY_DIR/" if [ -f "$ROOT_DIR/.npmrc" ]; then cp "$ROOT_DIR/.npmrc" "$RELAY_DIR/" fi echo "๐Ÿ“ฆ Installing prod node_modules into bundle via temp project" TMP_DEPLOY=$(mktemp -d /tmp/clawdis-deps.XXXXXX) cp "$ROOT_DIR/package.json" "$TMP_DEPLOY/" cp "$ROOT_DIR/pnpm-lock.yaml" "$TMP_DEPLOY/" [ -f "$ROOT_DIR/.npmrc" ] && cp "$ROOT_DIR/.npmrc" "$TMP_DEPLOY/" PNPM_STORE_DIR="$TMP_DEPLOY/.pnpm-store" \ PNPM_HOME="$HOME/Library/pnpm" \ pnpm install \ --prod \ --force \ --no-frozen-lockfile \ --ignore-scripts=false \ --config.enable-pre-post-scripts=true \ --config.ignore-workspace-root-check=true \ --config.shared-workspace-lockfile=false \ --config.node-linker=hoisted \ --lockfile-dir "$TMP_DEPLOY" \ --dir "$TMP_DEPLOY" PNPM_STORE_DIR="$TMP_DEPLOY/.pnpm-store" \ PNPM_HOME="$HOME/Library/pnpm" \ pnpm rebuild sharp --config.ignore-workspace-root-check=true --dir "$TMP_DEPLOY" rsync -a "$TMP_DEPLOY/node_modules/" "$RELAY_DIR/node_modules/" # Keep only the arm64 macOS sharp vendor payloads to shrink the bundle SHARP_VENDOR_DIR="$RELAY_DIR/node_modules/@img" if [ -d "$SHARP_VENDOR_DIR" ]; then find "$SHARP_VENDOR_DIR" -maxdepth 1 -type d -name "sharp-*" \ ! -name "sharp-darwin-arm64" \ ! -name "sharp-libvips-darwin-arm64" -exec rm -rf {} + fi # Prune obvious dev/build tooling to keep size down rm -rf \ "$RELAY_DIR/node_modules/.bin"/vite \ "$RELAY_DIR/node_modules/.bin"/rolldown \ "$RELAY_DIR/node_modules/.bin"/biome \ "$RELAY_DIR/node_modules/.bin"/vitest \ "$RELAY_DIR/node_modules/.bin"/tsc \ "$RELAY_DIR/node_modules/.bin"/tsx 2>/dev/null || true rm -rf \ "$RELAY_DIR/node_modules"/{vite,rolldown,vitest,ts-node,ts-node-dev,typescript,@types,docx-preview,jszip,lucide,ollama} 2>/dev/null || true rm -rf "$TMP_DEPLOY" else echo "๐Ÿงฐ Skipping relay payload packaging (SKIP_RELAY_PACKAGE=1)" fi if [ -f "$CLI_BIN" ]; then echo "๐Ÿ”ง Copying CLI helper" cp "$CLI_BIN" "$APP_ROOT/Contents/MacOS/ClawdisCLI" chmod +x "$APP_ROOT/Contents/MacOS/ClawdisCLI" fi echo "โน Stopping any running Clawdis" killall -q Clawdis 2>/dev/null || true echo "๐Ÿ” Signing bundle (auto-selects signing identity if SIGN_IDENTITY is unset)" "$ROOT_DIR/scripts/codesign-mac-app.sh" "$APP_ROOT" echo "โœ… Bundle ready at $APP_ROOT"