#!/bin/bash # Collect a privacy-conscious OutsideWindow diagnostic text file for launch support. set -uo pipefail APP_PATH="${APP_PATH:-/Applications/OutsideWindow.app}" BUNDLE_ID="${BUNDLE_ID:-com.outsidewindow.app}" LOG_WINDOW="${LOG_WINDOW:-30m}" DMG_PATH="${DMG_PATH:-$HOME/Downloads/OutsideWindow-latest.dmg}" OUTPUT_PATH="${OUTPUT_PATH:-$HOME/Desktop/outsidewindow-diagnostics-$(date +%Y%m%d-%H%M%S).txt}" PLIST_BUDDY="/usr/libexec/PlistBuddy" mkdir -p "$(dirname "$OUTPUT_PATH")" sanitize() { perl -pe ' s#(/Users/)[^/\s]+#$1#g; s/([?&](?:apiKey|apikey|key|token|access_token|latitude|longitude|lat|lon)=)[^&\s]+/${1}/gi; s#(/visualpasses/[0-9]+/)[-+]?[0-9]+(?:\.[0-9]+)?/[-+]?[0-9]+(?:\.[0-9]+)?#$1/#g; s/\b[-+]?(?:[0-8]?[0-9](?:\.[0-9]+)?|90(?:\.0+)?)\s*,\s*[-+]?(?:1[0-7][0-9](?:\.[0-9]+)?|[0-9]?[0-9](?:\.[0-9]+)?|180(?:\.0+)?)\b//g; s/([A-Z0-9._%+\-]+@[A-Z0-9.\-]+\.[A-Z]{2,})//gi; ' } if [ "${RUN_SANITIZER_SELF_TEST:-0}" = "1" ]; then sample='https://api.open-meteo.com/v1/forecast?latitude=30.2672&longitude=-97.7431&token=secret person@redaction.invalid /Users/alice/Desktop point 30.2672, -97.7431 /visualpasses/25544/30.2672/-97.7431/0/10&apiKey=secret' cleaned="$(printf '%s\n' "$sample" | sanitize)" if printf '%s\n' "$cleaned" | rg -q '30\.2672|-97\.7431|secret|person@redaction\.invalid|/Users/alice'; then printf 'sanitizer self-test failed\n' printf '%s\n' "$cleaned" exit 1 fi printf 'sanitizer self-test passed\n' exit 0 fi section() { printf '\n## %s\n\n' "$1" >>"$OUTPUT_PATH" } run_cmd() { local label="$1" shift printf '$ %s\n' "$label" >>"$OUTPUT_PATH" "$@" 2>&1 | sanitize >>"$OUTPUT_PATH" local status=${PIPESTATUS[0]} if [ "$status" -ne 0 ]; then printf '(command exited with status %s)\n' "$status" >>"$OUTPUT_PATH" fi printf '\n' >>"$OUTPUT_PATH" } { printf 'OutsideWindow Diagnostics\n' printf 'Generated: %s\n' "$(date)" printf 'App path: %s\n' "$APP_PATH" printf 'Bundle ID: %s\n' "$BUNDLE_ID" printf 'Log window: %s\n' "$LOG_WINDOW" printf '\nReview this file before sending it to support. It does not intentionally collect API keys, saved locations, environment variables, browser history, screenshots, or full system logs, but recent app log lines may still contain contextual details from the issue being reported.\n' } >"$OUTPUT_PATH" section "macOS" run_cmd "sw_vers" sw_vers run_cmd "uname -m" uname -m run_cmd "hardware summary" sh -c "system_profiler SPHardwareDataType 2>/dev/null | awk -F': ' '/Model Name|Model Identifier|Chip|Processor Name|Processor Speed|Total Number of Cores|Memory/ { print }'" section "Installed App" if [ -d "$APP_PATH" ]; then INFO_PLIST="$APP_PATH/Contents/Info.plist" run_cmd "CFBundleIdentifier" "$PLIST_BUDDY" -c "Print :CFBundleIdentifier" "$INFO_PLIST" run_cmd "CFBundleShortVersionString" "$PLIST_BUDDY" -c "Print :CFBundleShortVersionString" "$INFO_PLIST" run_cmd "CFBundleVersion" "$PLIST_BUDDY" -c "Print :CFBundleVersion" "$INFO_PLIST" run_cmd "codesign verify" codesign --verify --deep --strict --verbose=2 "$APP_PATH" run_cmd "codesign details" codesign -dv "$APP_PATH" run_cmd "spctl app assessment" spctl --assess --type execute --verbose=4 "$APP_PATH" else printf '%s is missing.\n' "$APP_PATH" >>"$OUTPUT_PATH" fi section "Running Process" run_cmd "pgrep OutsideWindow" pgrep -fl OutsideWindow section "Recent App Logs" if command -v log >/dev/null 2>&1; then run_cmd "log show --last $LOG_WINDOW --predicate subsystem == $BUNDLE_ID" log show --last "$LOG_WINDOW" --predicate "subsystem == \"$BUNDLE_ID\"" --style compact else printf 'macOS log command not found.\n' >>"$OUTPUT_PATH" fi section "Recent Provider Status Logs" if command -v log >/dev/null 2>&1; then run_cmd "provider refresh/stale cache logs" log show --last "$LOG_WINDOW" --predicate "subsystem == \"$BUNDLE_ID\" AND (eventMessage CONTAINS[c] \"Provider refresh failed\" OR eventMessage CONTAINS[c] \"Returning stale\")" --style compact else printf 'macOS log command not found.\n' >>"$OUTPUT_PATH" fi section "Recent Crash Reports" run_cmd "OutsideWindow crash report list" sh -c "ls -lt ~/Library/Logs/DiagnosticReports/*OutsideWindow* 2>/dev/null | head -n 10" section "Downloaded DMG" if [ -f "$DMG_PATH" ]; then run_cmd "shasum -a 256 $DMG_PATH" shasum -a 256 "$DMG_PATH" run_cmd "hdiutil verify $DMG_PATH" hdiutil verify "$DMG_PATH" run_cmd "spctl DMG assessment" spctl --assess --type open --context context:primary-signature --verbose=4 "$DMG_PATH" else printf 'No DMG found at %s. Set DMG_PATH=/path/to/OutsideWindow.dmg to include DMG checks.\n' "$DMG_PATH" >>"$OUTPUT_PATH" fi section "Support Notes" cat >>"$OUTPUT_PATH" <<'EOF' Please include: - What you were trying to do. - The exact step where the issue happened. - Whether this was a fresh install or an update. - Whether you used current location or a saved location. Do not send API keys, exact home addresses, or screenshots with private desktop content unless support specifically asks and you are comfortable sharing them. EOF printf 'Diagnostics written to %s\n' "$OUTPUT_PATH"