#!/bin/sh # Self State Connector installer for macOS (Apple silicon). # # curl -fsSL https://selfstate.si/install/connector.sh | sh # curl -fsSL https://selfstate.si/install/connector.sh | sh -s -- --code ABCD-EFGH # curl -fsSL https://selfstate.si/install/connector.sh | sh -s -- --uninstall # # What it does, for your macOS user only (no administrator password, no sudo): # 1. Downloads the Connector over HTTPS and checks its SHA-256 against the # value pinned in this script. A mismatch stops before anything is written. # 2. Unpacks it to ~/Library/Application Support/Self State Connector/. # 3. Adds a LaunchAgent (ai.kaizenstrategic.selfstate.connector) so it runs in # the background while you are logged in. Logs: ~/Library/Logs/Self State Connector/. # 4. Asks for the code shown on your phone and connects this Mac. # # Read this file before running it: https://selfstate.si/install/connector.sh set -eu CONNECTOR_VERSION="0.1.0" # PLACEHOLDER — set to the HTTPS address of SelfStateConnector-macos-arm64.tar.gz. CONNECTOR_URL="" # PLACEHOLDER — the SHA-256 printed by scripts/connector/build-bundle.mjs for that exact file. CONNECTOR_SHA256="" LABEL="ai.kaizenstrategic.selfstate.connector" LAUNCHCTL="${SELFSTATE_LAUNCHCTL:-/bin/launchctl}" APP_DIR="$HOME/Library/Application Support/Self State Connector" LOG_DIR="$HOME/Library/Logs/Self State Connector" PLIST="$HOME/Library/LaunchAgents/$LABEL.plist" CLI="$APP_DIR/current/bin/selfstate-connector" say() { printf '%s\n' "$*"; } fail() { printf '\nSelf State Connector: %s\n' "$*" >&2; exit 1; } tty_available() { [ -r /dev/tty ] && [ -w /dev/tty ] && (: /dev/null; } ask_yes() { # Piped from curl, standard input is this script: read answers from the terminal. tty_available || return 1 printf '%s' "$1" >/dev/tty read -r answer /dev/null | sed 's/^# \{0,1\}//'; exit 0 ;; *) fail "Unknown option: $1" ;; esac done [ "$(id -u)" != "0" ] || fail "Run this as your normal user, not with sudo or as root." [ "$(uname -s)" = "Darwin" ] || fail "This installer is for macOS. Windows and Linux are not available yet." if [ "$MODE" = "uninstall" ]; then say "This removes the Self State Connector from this Mac:" say " - stops the background service ($LABEL)" say " - deletes $PLIST" say " - deletes $APP_DIR (including the saved connection)" say " - deletes $LOG_DIR" if [ -z "$ASSUME_YES" ] && ! ask_yes "Remove it now? [y/N] "; then say "Nothing was removed."; exit 0; fi "$LAUNCHCTL" bootout "gui/$(id -u)/$LABEL" 2>/dev/null || true rm -f "$PLIST" rm -rf "$APP_DIR" "$LOG_DIR" say "Removed. In Self State on your phone, open Computer and tap Remove this computer to revoke its access too." exit 0 fi [ "$(uname -m)" = "arm64" ] || fail "This version needs a Mac with Apple silicon (M1 or later)." for tool in curl shasum tar; do command -v "$tool" >/dev/null 2>&1 || fail "$tool is missing from this Mac."; done case "$CONNECTOR_URL" in https://*) ;; *) fail "This installer has no download address yet. The Connector has not been published." ;; esac printf '%s' "$CONNECTOR_SHA256" | grep -Eq '^[0-9a-f]{64}$' || fail "This installer has no pinned checksum yet. The Connector has not been published." say "" say "Self State Connector $CONNECTOR_VERSION for macOS" say "" say "This will, for your user only (no administrator password):" say " 1. Download $CONNECTOR_URL" say " and check its SHA-256 is $CONNECTOR_SHA256" say " 2. Install it in $APP_DIR" say " 3. Add a background service that starts when you log in ($PLIST)" say " 4. Connect this Mac to Self State with the code shown on your phone" say "" say "It runs your agents' tasks on this Mac using the agent apps you have installed" say "(Codex, Claude Code, Gemini CLI, Grok). It does not share your screen." say "Remove it any time: curl -fsSL https://selfstate.si/install/connector.sh | sh -s -- --uninstall" say "" if [ -z "$ASSUME_YES" ]; then if tty_available; then ask_yes "Continue? [y/N] " || { say "Nothing was installed."; exit 0; } else fail "No terminal to confirm in. Re-run with --yes to proceed without the prompt."; fi fi WORK=$(mktemp -d "${TMPDIR:-/tmp}/selfstate-connector.XXXXXX") trap 'rm -rf "$WORK"' EXIT INT TERM ARCHIVE="$WORK/connector.tar.gz" say "Downloading…" curl --proto '=https' --tlsv1.2 -fsSL --retry 2 -o "$ARCHIVE" "$CONNECTOR_URL" || fail "The download failed. Check the internet connection and try again." say "Checking the SHA-256…" ACTUAL=$(shasum -a 256 "$ARCHIVE" | awk '{print $1}') [ "$ACTUAL" = "$CONNECTOR_SHA256" ] || fail "Checksum mismatch (got $ACTUAL). The download was not what this installer expects; nothing was installed." # Refuse archives with absolute paths or parent-directory entries. if tar -tzf "$ARCHIVE" | grep -Eq '(^/|(^|/)\.\.(/|$))'; then fail "The archive contains unsafe paths; nothing was installed."; fi mkdir -p "$WORK/unpacked" tar -xzf "$ARCHIVE" -C "$WORK/unpacked" [ -x "$WORK/unpacked/SelfStateConnector/bin/node" ] && [ -f "$WORK/unpacked/SelfStateConnector/lib/connector.mjs" ] || fail "The archive is incomplete; nothing was installed." say "Installing…" umask 077 mkdir -p "$APP_DIR/runtime" "$LOG_DIR" chmod 700 "$APP_DIR" "$LOG_DIR" DEST="$APP_DIR/runtime/$CONNECTOR_VERSION" rm -rf "$DEST.new" mv "$WORK/unpacked/SelfStateConnector" "$DEST.new" rm -rf "$DEST" mv "$DEST.new" "$DEST" ln -sfn "runtime/$CONNECTOR_VERSION" "$APP_DIR/current" # Give the background service the same PATH a new Terminal window gets, so it # finds Codex, Claude Code and the others where you installed them. sh "$CLI" capture-environment sh "$CLI" install-service say "" say "Installed. Now connect this Mac." say "On your phone, open Self State > Computer > Connect a computer to see a code." if [ -n "$CODE" ]; then sh "$CLI" connect --code "$CODE"; else sh "$CLI" connect; fi say "" say "Done. Manage it later with:" say " \"$CLI\" status | restart | run | logs | uninstall"