#!/bin/bash set -euo pipefail core_install_fail() { echo "BizClaw Core installer: $*" >&2; exit 1; } CORE_INSTALL_OS="$(uname -s)" case "$(uname -m)" in arm64|aarch64) CORE_INSTALL_ARCH=arm64 ;; x86_64|amd64) CORE_INSTALL_ARCH=x64 ;; *) core_install_fail 'No matching verified release is published for this CPU architecture.' ;; esac case "$CORE_INSTALL_OS" in Darwin) CORE_INSTALL_PLATFORM=macos [ "$(sw_vers -productVersion | cut -d. -f1)" -ge 14 ] || core_install_fail 'macOS 14 or later is required.' ;; Linux) CORE_INSTALL_PLATFORM=linux CORE_INSTALL_GLIBC="$(getconf GNU_LIBC_VERSION 2>/dev/null | awk '{print $2}')" || core_install_fail 'This release needs glibc Linux. Alpine/musl is not supported by this archive.' [ -n "$CORE_INSTALL_GLIBC" ] && awk -v v="$CORE_INSTALL_GLIBC" 'BEGIN{split(v,a,".");exit !(a[1]>2 || (a[1]==2 && a[2]>=35))}' || core_install_fail 'glibc 2.35 or later is required (Ubuntu 22.04+ / Debian 12+).' ;; *) core_install_fail 'No matching verified release is published for this system. Use Linux (including WSL2) or macOS.' ;; esac # Generated from verified, signed release artifacts. Installation always selects # a matching runtime, never a macOS binary on a Linux host. case "$CORE_INSTALL_PLATFORM-$CORE_INSTALL_ARCH" in macos-arm64) CORE_INSTALL_VERSION='1.4.0-preview.11' CORE_INSTALL_URL='https://downloads.bizclaw.com/releases/preview/1.4.0-preview.11/BizClaw-Core-1.4.0-preview.11-macos-arm64.tar.xz' CORE_INSTALL_SHA256='6c56c53728edfbaf74388cb5c76afdaa78590086243c2dba10be4f324bc947ff' CORE_INSTALL_SIGNATURE='SPaktGFofV29kq0kegB2QA7Ng9IiH/dhBJhz/vHD1f7vybqQAaDukW2EV0xTbDQw9rZkMsefgE+iVbnZFSFewvJ6YaRzUrSDba+Nrc7hSsw4anU8zhQ+LpfkjI3siDgzbFS2SGHZPtImnUAeTX5jzb66NECByOjF8Vr0M8+TMY8akRXIihb1zBaxl2xksb99vBFh+FYFpo3/v14wlIS7dMpQqd0kswx+Pp1SBQbwZV3BPGIpavgRv1anCgTzO63mR5/fUWSh7t03icgRoCB+EwBu9dR+gfKEFikVvkHO9nAU/sLlY+BSOuISYz2xuyPBKnfCZ5TZBSZ5jxbrxrJTGXapu9ivcgfCU2zITBEcwFacbBWV1L7DYvb7BwPkD/lW0c2GJBeKFFK1wJAfoZg09xev1SELUKnYhlSOtFtO2juAbmHyy1gN86DkD5JJoqyVsTNu1F++tzYBDygQIPHHrQQKTE9iJ4dHcnQdMmjwcnZnl3bcdkPhwb24WPxG5Wzz' ;; linux-x64) CORE_INSTALL_VERSION='1.4.0-preview.11' CORE_INSTALL_URL='https://downloads.bizclaw.com/releases/preview/1.4.0-preview.11/BizClaw-Core-1.4.0-preview.11-linux-x64.tar.xz' CORE_INSTALL_SHA256='b8670991920c224a907a0cfe818c3ef2889bf6de0a2d5b86c478754e3e03fdec' CORE_INSTALL_SIGNATURE='NGekJ77PsXx3vrCDk728pUbMg2WBG0CxSYBA3M//RDYk1pLrLr3Wpzlajul7+TZ3CaDGsCSj9T92bKT8MKYKeeggpzzTgZxFhGulnhWHrfupOcVHMSkKGCQWtxMKqiTIDkpuuBCEYf6dix7+1dFfs5A7wpRc/hX2M7PW454eLOhDKmTPNuGyAFavrmfbBv5C3vlHm5mbfTL2IQZ6Nj9NOT0XlOXSxEPkyqHTkU6F7ebQ3Q5q0lsgNx6fmD02boqXFdl2kiWWTiTdxWzJKw7rZvqn+biFVzlqI3Fy2lFrrgH3LULPNy4XpuI1f10edwbAVUrg8/b/jBUE54Ca2Ty9LHbuD7yR0pdjpGuNw7EFbSsTRETzPK8lZ4VOaCzhJXSaAbPeTs3/8Vtk2pUGP8ydHKuJfoPM7G5/LABgcp6q0QLKNketNchnKekrznQLxN/Dbk8Pf5pMLAP1vuFZASOca3LutJaiwCDQsftEtwCPDg7kyI8w3Ec42FF95lYNH/PN' ;; linux-arm64) CORE_INSTALL_VERSION='1.4.0-preview.11' CORE_INSTALL_URL='https://downloads.bizclaw.com/releases/preview/1.4.0-preview.11/BizClaw-Core-1.4.0-preview.11-linux-arm64.tar.xz' CORE_INSTALL_SHA256='b3dbb4754addefeebe89dd33c0d9ee8c7a880ce8d4acbd3e54c5e63ae7e7cdba' CORE_INSTALL_SIGNATURE='eWomuLc3UmE7cn6BWz0O/S3nhI9jE0AYXyzHe/+nWuJsjDYficC3ZK0zSpR42CtGiMDdlz0WEuoiGkfzqL+qhgotbibaRNxZw4msI2DRIMqTWbHNA2HHHfwo+JhM2SWPVdcadfR1CwlORaXsexhRmi30zpjljztrYMAooNUG7XJEjFQPMGagMVDUdaWnb4RLGOUJ81wcjmyewA3oZcnBUuMK+/7QPtYI0ByhaDFomCWzZo0tfljD+SY7qcNcVu2W3mBHblMZR2p15VFG03DDtlXUUYQSH3y5ruvVALN8QWcNysM/4yz+ClNXVDRWay7qIxcVnfHhxJMyQjRFhoqT7vgyahxVFyDL5YJVDviD2ZiNdnucb6dNKr9FvAyIHB12Xm13brKB467iTUuFMbFGZirxLGis8W84VYdCkq/4Zp2C2aUIfpbIvsiR1BKWvIBKLQNimMG9tfYHLp6D3WUWn5Hj6b7VUgVHtg+NXmzL37hbwg2JaHLfk4UUkm0NO8se' ;; *) core_install_fail 'No matching verified release is published for this system yet.' ;; esac for tool in curl tar xz openssl; do command -v "$tool" >/dev/null || core_install_fail "Missing system tool: $tool (Ubuntu: sudo apt install curl xz-utils openssl)"; done if command -v sha256sum >/dev/null; then CORE_INSTALL_HASH_TOOL=sha256sum; elif command -v shasum >/dev/null; then CORE_INSTALL_HASH_TOOL=shasum; else core_install_fail 'Missing SHA-256 verification tool.'; fi CORE_INSTALL_ROOT="${BIZCLAW_INSTALL_ROOT:-$HOME/.local/share/bizclaw-core}" CORE_INSTALL_BIN="${BIZCLAW_INSTALL_BIN:-$HOME/.local/bin}" case "$CORE_INSTALL_ROOT:$CORE_INSTALL_BIN" in *$'\n'*) core_install_fail 'Installation paths cannot contain newlines.' ;; esac [[ "$CORE_INSTALL_ROOT" = /* && "$CORE_INSTALL_BIN" = /* ]] || core_install_fail 'Installation paths must be absolute.' if [ "${BIZCLAW_INSTALL_DRY_RUN:-0}" = 1 ]; then printf 'BizClaw Core %s\nInstall: %s\nCommand: %s/bizclaw-core\nData: %s\nArtifact: %s\n' "$CORE_INSTALL_VERSION" "$CORE_INSTALL_ROOT" "$CORE_INSTALL_BIN" "${BIZCLAW_CORE_HOME:-$HOME/.bizclaw-core}" "$CORE_INSTALL_URL" exit 0 fi if [ "$CORE_INSTALL_OS" = Darwin ]; then [ "$(id -u)" -ne 0 ] || core_install_fail 'Run as your normal macOS user, without sudo.'; fi CORE_INSTALL_TMP="$(mktemp -d "${TMPDIR:-/tmp}/bizclaw-install.XXXXXX")" CORE_INSTALL_STAGING='' trap 'CORE_INSTALL_EXIT_CODE=$?; rm -rf "$CORE_INSTALL_TMP"; if [ -n "$CORE_INSTALL_STAGING" ]; then rm -rf "$CORE_INSTALL_STAGING"; fi; exit "$CORE_INSTALL_EXIT_CODE"' EXIT printf '\nInstalling BizClaw Core %s\n' "$CORE_INSTALL_VERSION" echo 'Downloading Core and its browser, document and OCR runtimes…' curl --fail --location --connect-timeout 30 --speed-limit 1 --speed-time 120 --max-time 3600 --proto '=https' --proto-redir '=https' --tlsv1.2 --retry 2 "$CORE_INSTALL_URL" --output "$CORE_INSTALL_TMP/core.tar.xz" "$CORE_INSTALL_TMP/release-public-key.pem" <<'KEY' -----BEGIN PUBLIC KEY----- MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAuDbuYiAu875nC1CXb/rF yWg8UHsn9YUPFfH69nbCwBMkZ/UJNFayItFra1Q55/FkoX4lh81qesgGCRJJf/5l xbg1Zr6bjr46jELw3i07J3RuQj7Cl/DzCk4y+9dqQ1zWULNzkiswvgKelXOer8ZM sH5NnUtqAnyNFbFNpDXulawKQGy2sYaFuyx9iWoyMdv1JCeR56lQ3Gc8KGB7snrV effnRuhJp6so48AxHRjGMLqAYPi099j1+kOLr47D5tY+8iLlkzQS7FRIQ9ZAKpwo epIZQ5foyUNFVGx9bkX2wM7HvW2p2UjrFtEdb7I8hUJq1XOsv7WBF96t3XFsLxbf qpwq87+MVD/VwDgOPcB7mAfMYKCu3ClBaIwDIXaDBKNRkZUcePSFKtqsWLKVFPNt wt/8CE22mIFPH9iMBY6GVAiqTGQCv0t1N0TPs0R8aPvyom5lEuZdyefgWYsZjB02 O2eCMY4iJV9jrhdoJ9TtxiGxBZFN7aMAdc3MwWu92AuRAgMBAAE= -----END PUBLIC KEY----- KEY printf '%s' "$CORE_INSTALL_SIGNATURE" | openssl base64 -d -A > "$CORE_INSTALL_TMP/signature.bin" openssl dgst -sha256 -verify "$CORE_INSTALL_TMP/release-public-key.pem" -signature "$CORE_INSTALL_TMP/signature.bin" "$CORE_INSTALL_TMP/core.tar.xz" >/dev/null || core_install_fail 'Release signature verification failed. Nothing installed.' echo 'SHA-256 and release signature verified.' tar -tf "$CORE_INSTALL_TMP/core.tar.xz" > "$CORE_INSTALL_TMP/entries.txt" while IFS= read -r entry; do case "$entry" in bizclaw-core|bizclaw-core/|bizclaw-core/*) ;; *) core_install_fail 'Unexpected archive layout.' ;; esac case "/$entry/" in */../*|*/./*) core_install_fail 'Unsafe archive path.' ;; esac done < "$CORE_INSTALL_TMP/entries.txt" mkdir -p "$CORE_INSTALL_ROOT/versions" "$CORE_INSTALL_BIN" CORE_INSTALL_STAGING="$(mktemp -d "$CORE_INSTALL_ROOT/versions/.install.XXXXXX")" COPYFILE_DISABLE=1 tar -xf "$CORE_INSTALL_TMP/core.tar.xz" -C "$CORE_INSTALL_STAGING" CORE_INSTALL_PAYLOAD="$CORE_INSTALL_STAGING/bizclaw-core" [ -x "$CORE_INSTALL_PAYLOAD/bizclaw-core" ] || core_install_fail 'Archive is missing the Core command.' if [ "$CORE_INSTALL_OS" = Linux ]; then bash "$CORE_INSTALL_PAYLOAD/scripts/cli/setup-linux.sh" --install "$CORE_INSTALL_PAYLOAD"; fi "$CORE_INSTALL_PAYLOAD/bizclaw-core" version > "$CORE_INSTALL_TMP/version.json" "$CORE_INSTALL_PAYLOAD/bin/bun" -e 'const meta=JSON.parse(await Bun.file(process.argv[1]).text()); if(meta.version!==process.argv[2]||meta.service!=="bizclaw-core")process.exit(1)' "$CORE_INSTALL_TMP/version.json" "$CORE_INSTALL_VERSION" || core_install_fail 'Installed runtime version check failed.' # Bootstrap a fixed updater from this signature-verified payload. The candidate # still performs the normal signed update transaction against the old install, # with backups/rollback and without downloading the same archive a second time. if [ -L "$CORE_INSTALL_ROOT/current" ] && [ -f "$CORE_INSTALL_ROOT/current/.archive-sha256" ]; then CORE_INSTALL_PREVIOUS_VERSION="$("$CORE_INSTALL_PAYLOAD/bin/bun" -e 'console.log(JSON.parse(await Bun.file(process.argv[1]).text()).version)' "$CORE_INSTALL_ROOT/current/package.json")" if [ "$CORE_INSTALL_PREVIOUS_VERSION" != "$CORE_INSTALL_VERSION" ]; then echo "Repairing the installed updater and upgrading ${CORE_INSTALL_PREVIOUS_VERSION} → ${CORE_INSTALL_VERSION}…" export BIZCLAW_SERVICE_COMMAND="$CORE_INSTALL_BIN/bizclaw-core" "$CORE_INSTALL_PAYLOAD/bizclaw-core" update --repair-install "$CORE_INSTALL_ROOT" --archive "$CORE_INSTALL_TMP/core.tar.xz" "$CORE_INSTALL_BIN/bizclaw-core" status exit 0 fi fi CORE_INSTALL_DEST="$CORE_INSTALL_ROOT/versions/$CORE_INSTALL_VERSION" if [ -e "$CORE_INSTALL_DEST" ]; then [ -f "$CORE_INSTALL_DEST/.archive-sha256" ] && [ "$(cat "$CORE_INSTALL_DEST/.archive-sha256")" = "$CORE_INSTALL_SHA256" ] || core_install_fail 'An unrecognized installation already uses this version directory; retained unchanged.' echo 'This verified release is already installed.' else printf '%s\n' "$CORE_INSTALL_SHA256" > "$CORE_INSTALL_PAYLOAD/.archive-sha256" mv "$CORE_INSTALL_PAYLOAD" "$CORE_INSTALL_DEST" fi if [ -e "$CORE_INSTALL_BIN/bizclaw-core" ] || [ -L "$CORE_INSTALL_BIN/bizclaw-core" ]; then [ -L "$CORE_INSTALL_BIN/bizclaw-core" ] && [ "$(readlink "$CORE_INSTALL_BIN/bizclaw-core")" = "$CORE_INSTALL_ROOT/current/bizclaw-core" ] || core_install_fail 'An existing unrelated bizclaw-core command was retained. Choose another BIZCLAW_INSTALL_BIN.' fi PYTHONHOME="$CORE_INSTALL_DEST/runtime/python" "$CORE_INSTALL_DEST/runtime/python/bin/python3" - "$CORE_INSTALL_ROOT" "$CORE_INSTALL_DEST" "$CORE_INSTALL_BIN" <<'PY' import os,sys root,dest,bin_dir=sys.argv[1:] for link,target in [(root+'/current',dest),(bin_dir+'/bizclaw-core',root+'/current/bizclaw-core')]: if os.path.lexists(link) and not os.path.islink(link): raise SystemExit('Refusing to replace a non-symlink installation path: '+link) temp=link+'.new.'+str(os.getpid()); os.symlink(target,temp); os.replace(temp,link) PY # Wire both interactive and login shells for local terminals and SSH sessions. if [ "${BIZCLAW_INSTALL_NO_PROFILE:-0}" != 1 ]; then CORE_INSTALL_PATH_LINE="$(printf 'case ":$PATH:" in *":%s:"*) ;; *) export PATH=%q:"$PATH" ;; esac' "$CORE_INSTALL_BIN" "$CORE_INSTALL_BIN")" CORE_INSTALL_PROFILES=() case "${SHELL:-/bin/bash}" in */zsh) CORE_INSTALL_PROFILES=("$HOME/.zshrc" "$HOME/.zprofile") ;; */fish) CORE_INSTALL_PROFILES=("$HOME/.config/fish/config.fish"); CORE_INSTALL_PATH_LINE="$(printf 'fish_add_path %q' "$CORE_INSTALL_BIN")" ;; *) CORE_INSTALL_PROFILES=("$HOME/.bashrc" "$HOME/.profile"); if [ -f "$HOME/.bash_profile" ]; then CORE_INSTALL_PROFILES+=("$HOME/.bash_profile"); fi ;; esac for CORE_INSTALL_PROFILE in "${CORE_INSTALL_PROFILES[@]}"; do mkdir -p "$(dirname "$CORE_INSTALL_PROFILE")" if ! grep -Fqx "$CORE_INSTALL_PATH_LINE" "$CORE_INSTALL_PROFILE" 2>/dev/null; then printf '\n# BizClaw Core CLI\n%s\n' "$CORE_INSTALL_PATH_LINE" >> "$CORE_INSTALL_PROFILE"; fi done fi printf 'Installed: %s\nCommand: %s/bizclaw-core\nBusiness data: %s\n' "$CORE_INSTALL_DEST" "$CORE_INSTALL_BIN" "${BIZCLAW_CORE_HOME:-$HOME/.bizclaw-core}" export BIZCLAW_SERVICE_COMMAND="$CORE_INSTALL_BIN/bizclaw-core" CORE_PACKAGE_ROOT="$CORE_INSTALL_DEST" source "$CORE_PACKAGE_ROOT/scripts/cli/network.sh" if [ "${BIZCLAW_INSTALL_NO_START:-0}" != 1 ]; then if [ "$CORE_INSTALL_OS" = Linux ]; then export XDG_RUNTIME_DIR="/run/user/$(id -u)" export DBUS_SESSION_BUS_ADDRESS="unix:path=$XDG_RUNTIME_DIR/bus" if command -v loginctl >/dev/null && [ "$(loginctl show-user "$(id -un)" -p Linger --value 2>/dev/null || true)" != yes ]; then echo 'Enabling the user background service to survive SSH logout and start after reboot.' if ! loginctl enable-linger "$(id -un)" 2>/dev/null; then if command -v sudo >/dev/null; then sudo -n true 2>/dev/null || sudo -v &2 exit 1 fi fi printf '\nOpen a new terminal to use bizclaw-core. In this terminal run:\n' printf ' export PATH=%q:"$PATH"\n' "$CORE_INSTALL_BIN" echo ' bizclaw-core version' echo ' bizclaw-core service status' core_admin_addresses echo 'Open a LAN Admin URL from another computer on the same network.'