#!/bin/sh # Yah Mesh desktop — one-command install for Linux, served by the relay: # # curl -fsSL https://relay.yahai.app/desktop | sh # # Installs the Yah Code service (Bun) as a systemd user service and the # Yah Mesh app (GTK4, Python). Everything prints — a silent installer once # cost this project an afternoon, so nothing here hides behind /dev/null. set -eu RELAY="https://relay.yahai.app" APP_DIR="${YAHCODE_APP_DIR:-$HOME/.yahcode/app}" say() { printf '%s\n' "→ $*"; } command -v curl >/dev/null 2>&1 || { echo "curl is required"; exit 1; } command -v tar >/dev/null 2>&1 || { echo "tar is required"; exit 1; } # ── macOS: the Yah AI app IS the manager — install it plus the service, then # hand off. Prebuilt and ad-hoc signed; the quarantine strip below is what # lets a downloaded ad-hoc app open without the right-click dance. if [ "$(uname)" = "Darwin" ]; then say "macOS detected — installing Yah AI.app + the Yah Code service" TMPD=$(mktemp -d) say "fetching the Mac app from ${RELAY}…" curl -fsSL "$RELAY/desktop-mac.zip" -o "$TMPD/yahai.zip" # ditto, NOT unzip — and for the same reason the deploy script uses ditto to # create it. unzip does not restore the bundle metadata ditto packs, so the # app arrived with a broken code-signature seal: `codesign --verify` on the # INSTALLED app reported "a sealed resource is missing or invalid" while the # downloaded archive verified perfectly. It still launched (ad-hoc, quarantine # stripped), which is exactly what makes it the kind of bug that ships. # ditto is part of the base system on every Mac — no new dependency. ditto -x -k "$TMPD/yahai.zip" "$TMPD" osascript -e 'quit app "Yah AI"' 2>/dev/null || true rm -rf "/Applications/Yah AI.app" mv "$TMPD/Yah AI.app" "/Applications/Yah AI.app" xattr -rd com.apple.quarantine "/Applications/Yah AI.app" 2>/dev/null || true say "app installed to /Applications" if command -v bun >/dev/null 2>&1; then say "bun $(bun --version) already installed" else say "installing bun (the service's runtime)…" curl -fsSL https://bun.sh/install | bash export PATH="$HOME/.bun/bin:$PATH" fi mkdir -p "$APP_DIR" curl -fsSL "$RELAY/desktop.tar.gz" | tar -xz -C "$APP_DIR" say "installing service dependencies (compiles llama.cpp once — minutes, not seconds)…" ( cd "$APP_DIR/yahcode" && "$(command -v bun)" install ) rm -rf "$TMPD" # ── the mesh machinery, in the same install ─────────────────────────────── # The Agents tab manages connectors through ~/.agentmesh/install-service.mjs # and friends. These used to arrive only with a SEPARATE connector install, # so a fresh desktop showed an Agents tab that could not act until a second # curl command nobody remembered. Seed the shared helpers here — but never # overwrite existing ones: a live connector's helpers belong to update.mjs. MESH_DIR="$HOME/.agentmesh" mkdir -p "$MESH_DIR" for helper in install-service.mjs pair.mjs doctor.mjs update.mjs mesh-mcp.mjs; do if [ ! -f "$MESH_DIR/$helper" ]; then curl -fsSL "$RELAY/$helper" -o "$MESH_DIR/$helper" say "mesh helper installed: $helper" fi done echo "" say "done. Open Yah AI (it starts and supervises the service itself)." say "Then pair from the phone: Yah AI → Settings → Yah Code → this Mac." open -a "Yah AI" 2>/dev/null || true exit 0 fi # ── bun: the runtime the service runs on ────────────────────────────────── if command -v bun >/dev/null 2>&1; then say "bun $(bun --version) already installed" else say "installing bun (the service's runtime)…" curl -fsSL https://bun.sh/install | bash export PATH="$HOME/.bun/bin:$PATH" command -v bun >/dev/null 2>&1 || { echo "bun install failed — see output above"; exit 1; } fi BUN="$(command -v bun)" # ── the payload: service source + the GTK app ───────────────────────────── say "fetching the desktop payload from ${RELAY}…" mkdir -p "$APP_DIR" curl -fsSL "$RELAY/desktop.tar.gz" | tar -xz -C "$APP_DIR" say "payload extracted to $APP_DIR" say "installing service dependencies (this compiles llama.cpp once — minutes, not seconds)…" ( cd "$APP_DIR/yahcode" && "$BUN" install ) # ── the service: systemd user unit, adopt-friendly ──────────────────────── UNIT_DIR="$HOME/.config/systemd/user" mkdir -p "$UNIT_DIR" cat > "$UNIT_DIR/yahcode.service" << UNIT [Unit] Description=Yah Code service (the API the Yah Mesh app and your phone talk to) [Service] ExecStart=$BUN run $APP_DIR/yahcode/src/main.ts WorkingDirectory=$APP_DIR/yahcode Restart=on-failure RestartSec=3 [Install] WantedBy=default.target UNIT if command -v systemctl >/dev/null 2>&1; then systemctl --user daemon-reload systemctl --user enable --now yahcode.service say "service enabled (systemctl --user status yahcode.service)" say "NOTE: to keep it running while logged out: loginctl enable-linger $USER" else say "no systemd — start manually with: $BUN run $APP_DIR/yahcode/src/main.ts" fi # ── the mesh machinery, in the same install ─────────────────────────────── # The Agents tab manages connectors through ~/.agentmesh/install-service.mjs # and friends. These used to arrive only with a SEPARATE connector install, # so a fresh desktop showed an Agents tab that could not act until a second # curl command nobody remembered. Seed the shared helpers here — but never # overwrite existing ones: a live connector's helpers belong to update.mjs. MESH_DIR="$HOME/.agentmesh" mkdir -p "$MESH_DIR" for helper in install-service.mjs pair.mjs doctor.mjs update.mjs mesh-mcp.mjs; do if [ ! -f "$MESH_DIR/$helper" ]; then curl -fsSL "$RELAY/$helper" -o "$MESH_DIR/$helper" say "mesh helper installed: $helper" fi done # ── the app: GTK4/libadwaita, launched as a module ──────────────────────── if python3 -c "import gi" >/dev/null 2>&1; then say "installing the Yah Mesh app launcher…" BIN_DIR="$HOME/.local/bin"; mkdir -p "$BIN_DIR" cat > "$BIN_DIR/yahmesh" << LAUNCH #!/bin/sh export PYTHONPATH="$APP_DIR/yahmesh:\${PYTHONPATH:-}" exec python3 -m yahmesh "\$@" LAUNCH chmod +x "$BIN_DIR/yahmesh" DESK_DIR="$HOME/.local/share/applications"; mkdir -p "$DESK_DIR" cat > "$DESK_DIR/yahmesh.desktop" << DESK [Desktop Entry] Type=Application Name=Yah Mesh Comment=Sessions, files, rooms and agents on this machine Exec=$BIN_DIR/yahmesh Terminal=false Categories=Development; DESK say "app installed — launch with: yahmesh (or from your app grid)" else say "PyGObject/GTK4 not found — the app needs it (Debian/Ubuntu: sudo apt install python3-gi gir1.2-adw-1; Fedora: sudo dnf install python3-gobject libadwaita)." say "The SERVICE is running regardless — your phone can pair and drive it now." fi echo "" say "done. Next: open Yah AI on your phone → Settings → Yah Code → pair this machine." say "In the app (or via curl http://127.0.0.1:8765/api/pair -X POST -d '{}'), mint the 6-digit code."