#!/bin/bash # Installs or updates Scene on a Mac, with nothing else to install first: # curl -fsSL https://askscene.vercel.app/install.sh | bash # Everything goes in ~/Scene, without administrator rights. It fetches its own copy of Node.js (checked # against the published hash), starts Scene whenever you log in, and opens the setup page. # To remove it all: # curl -fsSL https://askscene.vercel.app/install.sh | bash -s -- --uninstall set -euo pipefail SITE="${SCENE_SITE:-https://askscene.vercel.app}" HOME_DIR="${SCENE_HOME:-$HOME/Scene}" NODE_VERSION="24.22.2" LABEL="${SCENE_LABEL:-app.askscene.server}" PLIST="$HOME/Library/LaunchAgents/$LABEL.plist" MARKER="$HOME_DIR/.scene-install" say() { printf '\n\033[1m%s\033[0m\n' "$1"; } stop_here() { printf '\n%s\n' "$1" >&2; exit 1; } uninstall() { launchctl bootout "gui/$(id -u)/$LABEL" 2>/dev/null || true rm -f "$PLIST" # Only a folder this installer made: never someone's own folder that happens to be called Scene. if [ -e "$MARKER" ]; then rm -rf "$HOME_DIR"; fi say "Scene is removed." echo "One last step: in Chrome, go to chrome://extensions and click Remove on Scene." echo "If you signed in with ChatGPT, you can also remove Scene in ChatGPT settings." } main() { [ "${1:-}" = "--uninstall" ] && { uninstall; return; } [ "$(uname -s)" = "Darwin" ] || stop_here "Scene runs on Macs for now." case "$(uname -m)" in arm64) NODE_ARCH=arm64; NODE_SHA=aa9549349e47dffaa42dcf95d6e06ade8c4bdafc980979883b88767670df385a ;; x86_64) NODE_ARCH=x64; NODE_SHA=d5e336d588c45be0c37dbd52cc29895593009ca4997a77a1fd02815131ed0e52 ;; *) stop_here "Scene doesn't know this kind of Mac ($(uname -m))." ;; esac if [ -d "$HOME_DIR" ] && [ -n "$(ls -A "$HOME_DIR")" ] && [ ! -e "$MARKER" ]; then stop_here "You already have a folder called Scene in your home folder, and it isn't this app. Rename it, then paste the line again." fi say "Installing Scene. This takes a minute or two." mkdir -p "$HOME_DIR/.runtime" echo "Installed by Scene" > "$MARKER" TEMP="$(mktemp -d)" trap 'rm -rf "$TEMP"' EXIT NODE_DIR="$HOME_DIR/.runtime/node-$NODE_VERSION" if [ ! -x "$NODE_DIR/bin/node" ]; then echo "Getting Node.js, which Scene runs on…" curl -fsSL "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-darwin-$NODE_ARCH.tar.gz" -o "$TEMP/node.tar.gz" \ || stop_here "Couldn't download Node.js. Check your internet connection and paste the line again." [ "$(shasum -a 256 "$TEMP/node.tar.gz" | cut -d' ' -f1)" = "$NODE_SHA" ] || stop_here "The Node.js download didn't match its checksum, so it wasn't used. Paste the line again." mkdir -p "$TEMP/node" tar -xzf "$TEMP/node.tar.gz" -C "$TEMP/node" --strip-components 1 rm -rf "$NODE_DIR" mv "$TEMP/node" "$NODE_DIR" fi echo "Getting Scene…" curl -fsSL "$SITE/download/scene.zip" -o "$TEMP/scene.zip" || stop_here "Couldn't download Scene. Check your internet connection and paste the line again." unzip -q -o "$TEMP/scene.zip" -d "$TEMP/unpacked" # Your keys, sign-in and episode notes aren't in the download, so an update leaves them alone. cp -R "$TEMP/unpacked/scene/." "$HOME_DIR/" echo "Setting it up…" (cd "$HOME_DIR" && PATH="$NODE_DIR/bin:$PATH" npm install --omit=dev --no-audit --no-fund --loglevel=error) \ || stop_here "Setup didn't finish. Check your internet connection and paste the line again." "$NODE_DIR/bin/node" "$HOME_DIR/server/tunnel.mjs" --download >/dev/null 2>&1 || true if [ -z "${SCENE_NO_SERVICE:-}" ]; then mkdir -p "$(dirname "$PLIST")" cat > "$PLIST" < Label$LABEL ProgramArguments $NODE_DIR/bin/node --env-file-if-exists=.env server/index.mjs WorkingDirectory$HOME_DIR RunAtLoad KeepAlive ThrottleInterval30 StandardOutPath$HOME_DIR/scene.log StandardErrorPath$HOME_DIR/scene.log PLISTEOF launchctl bootout "gui/$(id -u)/$LABEL" 2>/dev/null || true launchctl bootstrap "gui/$(id -u)" "$PLIST" || stop_here "Scene couldn't start. Restart your computer and paste the line again." READY="" for _ in $(seq 1 30); do if curl -fsS -o /dev/null --max-time 1 "http://127.0.0.1:4787/api/health" 2>/dev/null; then READY=1; break; fi sleep 1 done [ -n "$READY" ] || stop_here "Scene didn't start. If another copy is already running, close it and paste the line again. Details: $HOME_DIR/scene.log" say "Scene is installed. Your browser is opening the next step." if [ -n "${SCENE_NO_OPEN:-}" ]; then echo "(Not opening anything.)" elif [ -d "/Applications/Google Chrome.app" ] || [ -d "$HOME/Applications/Google Chrome.app" ]; then open -a "Google Chrome" "http://127.0.0.1:4787/setup/" else open "http://127.0.0.1:4787/setup/" echo "Scene works in Google Chrome, which isn't on this Mac yet. Get it at https://www.google.com/chrome/" fi [ -n "${SCENE_NO_OPEN:-}" ] || open -R "$HOME_DIR/extension" || true else say "Scene is installed in $HOME_DIR." fi echo "You can close this window. Scene starts by itself whenever you log in." } # Called last, so a download cut off partway can't run half a script. main "$@"