#!/bin/bash # One-command Flow rinse launcher (macOS). Not headless — Brave must be logged in — # but fully unattended once the tab is open and Auto-start is ticked in the popup. # # ./launch.sh https://labs.google/fx/tools/flow/project/XXXXXXXX # FLOW_URL=... ./launch.sh # # Does three things: start the queue server if it's down, keep the Mac awake, and # open OR focus a single Flow tab in Brave (never spawns duplicates). # # Scheduled every 10 min via launchd for self-healing (see install.sh). # NOTE: launchd can't execute scripts inside ~/Documents (macOS TCC blocks it), # so the agent runs a COPY at "~/Library/Application Support/flowrinse/" — # after editing this file or local_server_example.py, re-run ./install.sh to resync. set -e DIR="$(cd "$(dirname "$0")" && pwd)" URL="${1:-$FLOW_URL}" if [ -z "$URL" ]; then echo "Usage: ./launch.sh (or set FLOW_URL)" exit 1 fi # 1. queue server on 8017 (nc check avoids consuming a task via /next-task) if ! nc -z localhost 8017 2>/dev/null; then ( cd "$DIR" && caffeinate -s python3 -B local_server_example.py >/tmp/flowrinse.log 2>&1 & ) echo "started queue server + caffeinate (log: /tmp/flowrinse.log)" sleep 1 else echo "queue server already up on 8017" fi # 2. open or focus ONE Flow tab (reuses an existing one so it can't double-run). # Only `activate` (steal system focus) when opening fresh — on the 10-min launchd # tick an existing tab is re-selected inside the browser without yanking you out of # whatever you're doing. Selecting the tab also keeps Memory Saver from discarding it. # # Must name the browser the EXTENSION is loaded into. ultra = Brave, m3ultra = Chrome. BROWSER="${FLOW_BROWSER:-Brave Browser}" # `open location` hands the URL to the DEFAULT browser (Brave here) — never to $BROWSER. # Make the tab inside the target app instead. Timeout so a wedged app can't hold the job: # an Automation denial surfaces as a ~2min hang then -1712, NOT as a clean -1743. if out=$(osascript <&1 with timeout of 30 seconds tell application "$BROWSER" set found to false repeat with w in windows set i to 0 repeat with t in tabs of w set i to i + 1 if (URL of t) contains "tools/flow/project" then set active tab index of w to i set index of w to 1 set found to true end if end repeat end repeat if not found then activate if (count of windows) = 0 then make new window tell window 1 to make new tab with properties {URL:"$URL"} end if end tell end timeout OSA ); then echo "$BROWSER focused on Flow. With Auto-start ticked, the rinse begins on load." else # Don't guess. The TCC row for (this launchd job's /bin/sh) -> (this browser) says which it is. echo "cannot drive $BROWSER: $out" BUNDLE=$(osascript -e "id of app \"$BROWSER\"" 2>/dev/null) # no AppleEvent — reads the bundle DB="$HOME/Library/Application Support/com.apple.TCC/TCC.db" AUTH=$(sqlite3 "$DB" "select auth_value from access where service='kTCCServiceAppleEvents' and client='/bin/sh' and indirect_object_identifier='$BUNDLE';" 2>/dev/null) case "$AUTH" in 0) echo " → Automation is DENIED for /bin/sh -> $BUNDLE (someone clicked Don't Allow)." \ "Fix: System Settings > Privacy & Security > Automation > sh > enable \"$BROWSER\"." ;; 2) echo " → Automation is ALLOWED, so $BROWSER itself is not answering AppleEvents." \ "Screen locked? $(ioreg -n Root -d1 -r | tr -d ' ' | grep -q '"CGSSessionScreenIsLocked"=Yes' \ && echo yes || echo no). Try quitting and reopening $BROWSER." ;; *) echo " → No Automation grant yet for /bin/sh -> $BUNDLE: a consent dialog is waiting on" \ "$(hostname -s)'s screen. Unlock it and click Allow." ;; esac exit 0 # not an error — the 10-min tick retries once a human fixes it fi