#!/bin/sh
#
# get-synapcores-ce.sh — one-line remote installer for SynapCores
# Community Edition.
#
# Hosted at: https://get.synapcores.com
#
# Usage:
# curl -fsSL https://get.synapcores.com/install.sh | sh
#
# Quick userland test (no sudo, no system service, no /opt or /etc):
# curl -fsSL https://get.synapcores.com/install.sh | SYNAPCORES_USER_INSTALL=1 sh
#
# Or, to pin a specific release:
# curl -fsSL https://get.synapcores.com/install.sh | SYNAPCORES_VERSION=v1.2.0 sh
#
# Environment variables:
# SYNAPCORES_USER_INSTALL=1 Install for the current user only:
# binary → ~/.local/bin, config + data →
# ~/.synapcores, no system user, no
# systemd unit. Good for kicking the
# tires without touching system state.
# SYNAPCORES_PREFIX=
Override the binary install dir
# (default: /usr/local/bin in system
# mode, ~/.local/bin in user mode).
# SYNAPCORES_BINARY_ONLY=1 Drop the binary and stop. Operator
# handles config and service themselves.
# SYNAPCORES_VERSION= Pin a specific release tag.
# SYNAPCORES_NONINTERACTIVE=1 Skip prompts; assume safe defaults.
# SYNAPCORES_WITH_NLI=1 Install and enable semantic verification.
# SYNAPCORES_NLI_MODEL=... Curated alias (default nli-default; nli-xsmall optional).
# SYNAPCORES_NO_MODEL_DOWNLOAD=1 Use previously imported models, without pulling.
#
# Written in POSIX sh on purpose: when invoked via `curl ... | sh`,
# Debian/Ubuntu's /bin/sh is dash, not bash. Bash-only constructs like
# `[[ ... ]]`, `set -o pipefail`, `local`, `$EUID`, and arrays MUST NOT
# appear in this file. If you need them, branch out into a helper that
# bash can run after the binary is on disk.
#
# What it does:
# 1. Detects the OS and architecture
# 2. Resolves the latest GitHub release tag (or honors $SYNAPCORES_VERSION)
# 3. Downloads the matching binary tarball + checksum
# 4. Verifies the checksum
# 5. Drops the binary at /usr/local/bin/synapcores (system mode,
# requires sudo) or ~/.local/bin/synapcores (user mode, no sudo).
# 6. Hands off to install-ce.sh for setup (system: useradd, /opt
# layout, /etc/systemd unit; user: ~/.synapcores layout, no
# systemd unit). Skipped entirely if SYNAPCORES_BINARY_ONLY is set.
#
# Requirements:
# - curl
# - tar
# - sha256sum (or shasum -a 256)
set -eu
# ---------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------
GITHUB_REPO="${SYNAPCORES_REPO:-SynapCores/synapcores-releases}"
RELEASE_BASE="https://github.com/${GITHUB_REPO}/releases"
PINNED_VERSION="${SYNAPCORES_VERSION:-}"
BINARY_ONLY="${SYNAPCORES_BINARY_ONLY:-}"
# User-mode install: opt in via SYNAPCORES_USER_INSTALL=1, or via
# interactive prompt (Linux only) when stdin is a tty and the operator
# hasn't set the env var. Default = system mode.
USER_INSTALL="${SYNAPCORES_USER_INSTALL:-}"
# INSTALL_PREFIX default depends on USER_INSTALL — resolved after the
# (possibly interactive) prompt below so we honor a late opt-in.
INSTALL_PREFIX="${SYNAPCORES_PREFIX:-}"
NLI_OPT_IN="${SYNAPCORES_WITH_NLI:-0}"
NLI_MODEL="${SYNAPCORES_NLI_MODEL:-nli-default}"
# ---------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------
log() { printf '\033[1;34m[get-synapcores]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[get-synapcores]\033[0m %s\n' "$*" >&2; }
fail() { printf '\033[1;31m[get-synapcores]\033[0m %s\n' "$*" >&2; exit 1; }
case "$NLI_OPT_IN" in
1|true|yes) NLI_OPT_IN=1 ;;
0|false|no|"") NLI_OPT_IN=0 ;;
*) fail "SYNAPCORES_WITH_NLI must be 0 or 1" ;;
esac
case "$NLI_MODEL" in
nli-default|nli-small|nli-xsmall) ;;
*) fail "Unknown NLI model; use nli-default or nli-xsmall" ;;
esac
require() {
command -v "$1" >/dev/null 2>&1 || fail "missing required tool: $1"
}
require curl
require tar
if command -v sha256sum >/dev/null 2>&1; then
SHA256_TOOL="sha256sum"
elif command -v shasum >/dev/null 2>&1; then
SHA256_TOOL="shasum -a 256"
else
fail "missing required tool: sha256sum (or shasum)"
fi
# ---------------------------------------------------------------------
# Platform detection
# ---------------------------------------------------------------------
# detect_platform: prints "-" to stdout. Exits non-zero if
# the platform is unsupported. POSIX sh has no `local`, so we use
# uppercase variable names to make it obvious these survive the call.
detect_platform() {
DETECTED_OS=$(uname -s)
DETECTED_ARCH=$(uname -m)
case "$DETECTED_OS" in
Linux) DETECTED_OS=linux ;;
Darwin) DETECTED_OS=darwin ;;
*) fail "unsupported OS: $DETECTED_OS (CE binaries are published for Linux and macOS)" ;;
esac
case "$DETECTED_ARCH" in
x86_64|amd64) DETECTED_ARCH=x86_64 ;;
aarch64|arm64) DETECTED_ARCH=aarch64 ;;
*) fail "unsupported architecture: $DETECTED_ARCH" ;;
esac
printf '%s-%s\n' "$DETECTED_OS" "$DETECTED_ARCH"
}
# perf-wave-10B: detect_gpu — pick between CPU and CUDA tarball variants.
#
# Sets DETECTED_GPU to "cpu" or "cuda". Override via SYNAPCORES_GPU env
# var:
# SYNAPCORES_GPU=cpu curl ... | sh (force CPU even on a GPU host)
# SYNAPCORES_GPU=cuda curl ... | sh (skip detection, demand CUDA)
#
# CUDA variant is published only for linux-x86_64. macOS / aarch64
# always resolve to "cpu" — Apple Silicon uses Metal (separate codepath),
# and we don't ship aarch64 CUDA tarballs.
detect_gpu() {
DETECTED_GPU=cpu
# Allow explicit override before we run any detection.
if [ -n "${SYNAPCORES_GPU:-}" ]; then
DETECTED_GPU="$SYNAPCORES_GPU"
log "GPU override: SYNAPCORES_GPU=${SYNAPCORES_GPU}"
return
fi
# No CUDA build for macOS or for aarch64 — Apple uses Metal, and
# we don't currently publish aarch64 CUDA tarballs.
if [ "$DETECTED_OS" = "darwin" ]; then return; fi
if [ "$DETECTED_ARCH" = "aarch64" ]; then return; fi
# Primary signal: nvidia-smi works (driver is loaded + GPU visible).
if command -v nvidia-smi >/dev/null 2>&1; then
if nvidia-smi >/dev/null 2>&1; then
DETECTED_GPU=cuda
log "NVIDIA GPU detected via nvidia-smi"
return
fi
fi
# Fallback: libcuda.so.1 present (covers headless servers without
# nvidia-smi installed but with the runtime driver lib in place).
if [ -f /usr/lib/x86_64-linux-gnu/libcuda.so.1 ] \
|| [ -f /usr/lib64/libcuda.so.1 ] \
|| [ -f /usr/lib/libcuda.so.1 ]; then
DETECTED_GPU=cuda
log "CUDA runtime library (libcuda.so.1) detected"
return
fi
log "No CUDA detected — using CPU build"
}
# detect_intel_mac_unsupported: bail out on Intel Macs (no native binary
# published). Split out from detect_platform() because POSIX `$(...)` runs
# the function in a subshell and `exit` from there only kills the subshell.
detect_intel_mac_unsupported() {
# Intel Mac native binary is not published as of v1.3.0-ce. The
# source migration to ffmpeg-next 7 unblocked the build, but
# GitHub-hosted macos-13 runner availability has been unreliable
# on personal-account repos (multi-hour queue waits with no
# allocation). Apple Silicon (macos-14) builds reliably, so we
# ship Apple Silicon native + recommend Docker for Intel Macs.
if [ "$DETECTED_OS" = "darwin" ] && [ "$DETECTED_ARCH" = "x86_64" ]; then
cat >&2 <&2 <&2 <&2 <&2 <&2 <) to extract the tag.
PINNED_VERSION=$(
curl -fsS -o /dev/null -w '%{redirect_url}' "${RELEASE_BASE}/latest" \
| sed 's@^.*/tag/@@'
)
[ -n "$PINNED_VERSION" ] || fail "could not resolve latest release"
fi
log "Version: $PINNED_VERSION"
# ---------------------------------------------------------------------
# Anonymous install counter ping (no PII, opt-out via COUNTER_OPT_OUT=1)
# ---------------------------------------------------------------------
# One fire-and-forget GET after platform + version are resolved and
# before the download starts. curl-pipe-sh leaves no trace on GitHub
# Pages, so this is the only way we can count CLI installs. Sends only
# the version tag, OS and arch (already public in the tarball URL).
# 5-second timeout; `|| true` means a down counter never blocks install.
if [ -z "${COUNTER_OPT_OUT:-}" ]; then
_counter_url="https://synapcores.com/api/install-counter?v=${PINNED_VERSION}&os=${DETECTED_OS}&arch=${DETECTED_ARCH}&channel=cli"
curl -fsSL --max-time 5 -o /dev/null "$_counter_url" 2>/dev/null || true
fi
# ---------------------------------------------------------------------
# Download
# ---------------------------------------------------------------------
# DISTRO_TAG is "" on Ubuntu 22.04 / Debian 12 / macOS / unknown distros
# (FFmpeg 4 / native Mac tarball) and "-ubuntu24" on Ubuntu 24.04 / Debian 13
# (FFmpeg 6 tarball variant). Set by check_distro() above.
#
# perf-wave-10B: GPU_TAG is "-cuda" when DETECTED_GPU=cuda (libcuda
# present or explicit override) and "" otherwise. The CUDA variant is
# only published for linux-x86_64; detect_gpu() already short-circuits
# to "cpu" elsewhere, so this concat is safe everywhere.
GPU_TAG=""
if [ "$DETECTED_GPU" = "cuda" ]; then
GPU_TAG="-cuda"
# Not every release ships a CUDA tarball — the linux-x86_64-cuda
# build OOM-kills the 16 GB GitHub-hosted runner during the LTO
# link step (see release-ce.yml note on CARGO_PROFILE_RELEASE_LTO).
# When the GPU choice was auto-detected, probe the release asset
# before committing to the CUDA tarball; on 404, fall back to CPU.
# An explicit SYNAPCORES_GPU=cuda override is honored — we let the
# download fail loudly so the operator notices instead of silently
# getting a CPU binary.
if [ "$GPU_AUTO_DETECTED" = "true" ]; then
CUDA_TARBALL="synapcores-ce-${PINNED_VERSION}-${PLATFORM}${DISTRO_TAG}-cuda.tar.gz"
CUDA_URL="${RELEASE_BASE}/download/${PINNED_VERSION}/${CUDA_TARBALL}"
if curl -sfI "$CUDA_URL" >/dev/null 2>&1; then
log "CUDA tarball confirmed for ${PINNED_VERSION}"
else
warn "CUDA tarball not published for ${PINNED_VERSION} — falling back to CPU build"
DETECTED_GPU=cpu
GPU_TAG=""
fi
fi
fi
TARBALL="synapcores-ce-${PINNED_VERSION}-${PLATFORM}${DISTRO_TAG}${GPU_TAG}.tar.gz"
TARBALL_URL="${RELEASE_BASE}/download/${PINNED_VERSION}/${TARBALL}"
CHECKSUM_URL="${TARBALL_URL}.sha256"
WORK_DIR=$(mktemp -d)
trap 'rm -rf "$WORK_DIR"' EXIT INT TERM
log "Downloading ${TARBALL}..."
curl -fsSL "$TARBALL_URL" -o "${WORK_DIR}/${TARBALL}" \
|| fail "download failed: $TARBALL_URL"
log "Downloading checksum..."
curl -fsSL "$CHECKSUM_URL" -o "${WORK_DIR}/${TARBALL}.sha256" \
|| fail "checksum download failed: $CHECKSUM_URL"
# ---------------------------------------------------------------------
# Verify
# ---------------------------------------------------------------------
log "Verifying checksum..."
(
cd "$WORK_DIR"
EXPECTED=$(awk '{print $1}' "${TARBALL}.sha256")
ACTUAL=$($SHA256_TOOL "${TARBALL}" | awk '{print $1}')
if [ "$EXPECTED" != "$ACTUAL" ]; then
fail "checksum mismatch: expected $EXPECTED got $ACTUAL"
fi
)
log "Checksum OK."
# ---------------------------------------------------------------------
# Extract
# ---------------------------------------------------------------------
log "Extracting..."
tar -xzf "${WORK_DIR}/${TARBALL}" -C "$WORK_DIR"
# The workflow packages the binary inside a single top-level dir:
# synapcores-ce--/synapcores
# Locate it without hardcoding the dir name so the script keeps
# working if the packaging convention changes.
BINARY_SRC=$(find "$WORK_DIR" -maxdepth 3 -type f -name synapcores -perm -u+x 2>/dev/null | head -1)
if [ -z "$BINARY_SRC" ] || [ ! -x "$BINARY_SRC" ]; then
fail "extracted archive does not contain a 'synapcores' executable"
fi
# ---------------------------------------------------------------------
# Install binary
# ---------------------------------------------------------------------
# POSIX sh has no $EUID — use `id -u`. Also POSIX has no arrays, so we
# use SUDO_PREFIX as a (possibly empty) string that is word-split into
# argv when the install command runs.
#
# User mode never needs sudo — the binary lands in the user's own
# ~/.local/bin (or whatever they set via SYNAPCORES_PREFIX).
if [ -n "$USER_INSTALL" ] || [ "$(id -u)" -eq 0 ]; then
SUDO_PREFIX=""
else
log "Installing to ${INSTALL_PREFIX} requires sudo..."
SUDO_PREFIX="sudo"
fi
# shellcheck disable=SC2086
$SUDO_PREFIX install -m 0755 "$BINARY_SRC" "${INSTALL_PREFIX}/synapcores" \
|| fail "binary install failed"
log "Installed: ${INSTALL_PREFIX}/synapcores"
# ONNX Runtime is loaded relative to the installed executable. Preserve it
# before WORK_DIR is deleted, including macOS, user and binary-only installs.
# Existing tarballs without an ONNX manifest keep their previous capability;
# a new manifest with missing runtime/attribution is a broken package.
ONNX_PACKAGE_DIR="$(dirname "$BINARY_SRC")"
if [ -f "${ONNX_PACKAGE_DIR}/onnx-runtime-manifest.json" ]; then
case "$DETECTED_OS" in
linux) ONNX_LIBRARY_NAME="libonnxruntime.so" ;;
darwin) ONNX_LIBRARY_NAME="libonnxruntime.dylib" ;;
esac
ONNX_RUNTIME_DIR="${INSTALL_PREFIX}/lib/synapcores"
ONNX_NOTICE_DIR="${INSTALL_PREFIX}/share/synapcores/licenses/onnxruntime"
[ -r "${ONNX_PACKAGE_DIR}/lib/${ONNX_LIBRARY_NAME}" ] \
|| fail "ONNX package is missing ${ONNX_LIBRARY_NAME}"
for ONNX_NOTICE in LICENSE ThirdPartyNotices.txt VERSION_NUMBER; do
[ -r "${ONNX_PACKAGE_DIR}/licenses/onnxruntime/${ONNX_NOTICE}" ] \
|| fail "ONNX package is missing native attribution: ${ONNX_NOTICE}"
done
# shellcheck disable=SC2086
$SUDO_PREFIX mkdir -p "$ONNX_RUNTIME_DIR" "$ONNX_NOTICE_DIR" \
|| fail "cannot create ONNX runtime install directories"
# macOS loader dependencies/symlink targets travel as one runtime folder.
if [ "$DETECTED_OS" = darwin ]; then
$SUDO_PREFIX cp -R "${ONNX_PACKAGE_DIR}/lib/." "$ONNX_RUNTIME_DIR/" \
|| fail "macOS runtime folder install failed"
fi
for ONNX_LIBRARY in "${ONNX_PACKAGE_DIR}"/lib/libonnxruntime*.so* "${ONNX_PACKAGE_DIR}"/lib/libonnxruntime*.dylib; do
[ -f "$ONNX_LIBRARY" ] || continue
# shellcheck disable=SC2086
$SUDO_PREFIX install -m 0644 "$ONNX_LIBRARY" "${ONNX_RUNTIME_DIR}/$(basename "$ONNX_LIBRARY")" \
|| fail "ONNX runtime library install failed"
done
for ONNX_NOTICE in LICENSE ThirdPartyNotices.txt VERSION_NUMBER; do
# shellcheck disable=SC2086
$SUDO_PREFIX install -m 0644 "${ONNX_PACKAGE_DIR}/licenses/onnxruntime/${ONNX_NOTICE}" "${ONNX_NOTICE_DIR}/${ONNX_NOTICE}" \
|| fail "ONNX runtime attribution install failed"
done
# shellcheck disable=SC2086
$SUDO_PREFIX install -m 0644 "${ONNX_PACKAGE_DIR}/onnx-runtime-manifest.json" "${ONNX_RUNTIME_DIR}/onnx-runtime-manifest.json" \
|| fail "ONNX runtime provenance install failed"
log "Installed CPU ONNX runtime: ${ONNX_RUNTIME_DIR}/${ONNX_LIBRARY_NAME}"
fi
# The checksum-verified package carries the platform startup helper. It is
# installed beside the executable for relocatable foreground/launchd/systemd use.
START_HELPER_SRC="${ONNX_PACKAGE_DIR}/synapcores-start.sh"
if [ -f "$START_HELPER_SRC" ]; then
$SUDO_PREFIX install -m 0755 "$START_HELPER_SRC" "${INSTALL_PREFIX}/synapcores-start" \
|| fail "startup helper install failed"
fi
# In user mode, warn if the install dir isn't on PATH. ~/.local/bin is
# included by ~/.profile on most modern distros, but only after the
# next login — so a fresh-from-`useradd` account might not see it yet.
if [ -n "$USER_INSTALL" ]; then
case ":${PATH}:" in
*":${INSTALL_PREFIX}:"*)
: # already on PATH
;;
*)
warn "${INSTALL_PREFIX} is not on your PATH for this shell."
warn "Run synapcores via its full path, or add to your shell rc:"
warn " export PATH=\"${INSTALL_PREFIX}:\$PATH\""
;;
esac
fi
# ---------------------------------------------------------------------
# Verify install (deferred on both OSes)
# ---------------------------------------------------------------------
#
# Skipped here intentionally. The CE binary is dynamically linked
# against FFmpeg / Tesseract / Leptonica that aren't installed yet —
# - macOS: Homebrew may not have them installed
# - Linux: install-ce.sh runs apt-get for them next
# So `synapcores --version` would fail with "error while loading
# shared libraries: libavutil.so.56" on a fresh box and the
# resulting "binary did not report 'Community'" warning would mislead
# the operator into thinking the wrong edition was downloaded.
# install-ce.sh does the version check AFTER its install_runtime_deps
# step, which is the right point.
# ---------------------------------------------------------------------
# System setup (or skip)
# ---------------------------------------------------------------------
if [ -n "$BINARY_ONLY" ]; then
log "SYNAPCORES_BINARY_ONLY set; skipping system setup."
exit 0
fi
# macOS path: detect missing Homebrew deps and offer to install them.
# install-ce.sh is a Linux installer (useradd, apt-get, systemd) and
# would fail with "useradd: command not found" on macOS — we don't
# call it here.
#
# The CE macOS binary is dynamically linked against Homebrew's
# ffmpeg, tesseract, and leptonica. Without them the binary's
# dynamic linker fails silently and `--version` returns nothing.
#
# Set SYNAPCORES_NONINTERACTIVE=1 to skip the prompt and install
# missing deps automatically (CI / automation).
if [ "$DETECTED_OS" = "darwin" ]; then
if ! command -v brew >/dev/null 2>&1; then
warn "Homebrew is required for macOS runtime deps (ffmpeg/tesseract/leptonica)."
warn "Install Homebrew first: https://brew.sh"
warn "Then re-run: curl -fsSL https://get.synapcores.com/install.sh | sh"
exit 1
fi
# Pick the right ffmpeg formula. We try ffmpeg@7 first because
# that's what the GitHub Actions release built against; if it's
# not in the user's Homebrew tap we fall back to plain ffmpeg
# (ffmpeg-next 7 supports both 7.x and 8.x at runtime).
if brew info ffmpeg@7 >/dev/null 2>&1; then
FFMPEG_FORMULA="ffmpeg@7"
else
FFMPEG_FORMULA="ffmpeg"
fi
REQUIRED_DEPS="$FFMPEG_FORMULA tesseract leptonica"
MISSING_DEPS=""
for dep in $REQUIRED_DEPS; do
if ! brew list --formula "$dep" >/dev/null 2>&1; then
MISSING_DEPS="$MISSING_DEPS $dep"
fi
done
# Strip leading whitespace
MISSING_DEPS=$(printf '%s' "$MISSING_DEPS" | sed 's/^ *//')
if [ -n "$MISSING_DEPS" ]; then
log "Missing Homebrew deps: $MISSING_DEPS"
if [ -n "${SYNAPCORES_NONINTERACTIVE:-}" ]; then
log "Non-interactive mode (SYNAPCORES_NONINTERACTIVE set); installing..."
ANSWER="y"
elif [ ! -t 0 ] && [ ! -e /dev/tty ]; then
warn "stdin is not a terminal and /dev/tty is unavailable; cannot prompt."
warn "Install manually: brew install $MISSING_DEPS"
warn "Or set SYNAPCORES_NONINTERACTIVE=1 to install automatically."
exit 1
else
printf '\033[1;34m[get-synapcores]\033[0m Install them now? (brew install %s) [Y/n] ' "$MISSING_DEPS"
# Read from /dev/tty so the prompt works under
# `curl ... | sh` where stdin is the script itself.
read -r ANSWER < /dev/tty || ANSWER="n"
fi
case "$ANSWER" in
""|[Yy]*)
log "Running: brew install $MISSING_DEPS"
# shellcheck disable=SC2086
brew install $MISSING_DEPS || {
warn "brew install failed. Install manually: brew install $MISSING_DEPS"
exit 1
}
;;
*)
log "Skipping. Install manually: brew install $MISSING_DEPS"
log "Then run: ${INSTALL_PREFIX}/synapcores --version"
exit 0
;;
esac
else
log "All Homebrew runtime deps present."
fi
# With deps installed, the binary should now run.
if "${INSTALL_PREFIX}/synapcores" --version 2>/dev/null | grep -q "Community"; then
log "Edition check: $("${INSTALL_PREFIX}/synapcores" --version)"
else
warn "Binary still won't run cleanly. Diagnose with:"
warn " otool -L ${INSTALL_PREFIX}/synapcores | head -20"
warn "Look for any 'not found' lines pointing at missing dylibs."
fi
# ----- Drop default config + data dir -----
SC_HOME="${HOME}/.synapcores"
SC_CONFIG="${SC_HOME}/gateway.toml"
SC_DATA_DIR="${AIDB_DATA_DIR:-${SC_HOME}/data}"
case "$SC_DATA_DIR" in
/*) ;;
*) fail "AIDB_DATA_DIR must be an absolute path for installation" ;;
esac
SC_MODELS_DIR="${SC_HOME}/models/text"
mkdir -p "$SC_HOME" "$SC_DATA_DIR" "$SC_MODELS_DIR"
chmod 700 "$SC_HOME"
START_HELPER="${INSTALL_PREFIX}/synapcores-start"
[ -x "$START_HELPER" ] || fail "release is missing synapcores-start.sh; install a complete current release folder"
# The bundled template (v1.3.1+) lives next to the binary in the
# extracted tarball. Older tarballs don't have it, so fall through
# to the inline minimal config in that case.
BUNDLED_TEMPLATE="$(dirname "$BINARY_SRC")/community.toml.template"
if [ -f "$SC_CONFIG" ]; then
log "Config already exists at ${SC_CONFIG} — leaving as-is."
elif [ -f "$BUNDLED_TEMPLATE" ]; then
# Adapt the template for macOS: rewrite the data_dir to live
# under the user's home (the template defaults to
# /opt/synapcores/aidb_data, which would need root and isn't
# the macOS convention).
SC_DATA_TOML=$(printf %s "$SC_DATA_DIR" | sed 's/\\/\\\\/g; s/"/\\"/g')
while IFS= read -r template_line || [ -n "$template_line" ]; do
case "$template_line" in
data_dir\ =*) printf 'data_dir = "%s"\n' "$SC_DATA_TOML" ;;
*) printf '%s\n' "$template_line" ;;
esac
done < "$BUNDLED_TEMPLATE" > "$SC_CONFIG"
log "Wrote default config to ${SC_CONFIG}"
else
warn "No config template bundled (older tarball?). Generating minimal config."
SC_DATA_TOML=$(printf %s "$SC_DATA_DIR" | sed 's/\\/\\\\/g; s/"/\\"/g')
cat >"$SC_CONFIG" </dev/null; then
chmod +x "$INSTALLER"
set -- "$INSTALLER" --binary "${INSTALL_PREFIX}/synapcores"
if [ "$NLI_OPT_IN" = 1 ]; then
set -- "$@" --with-nli --nli-model "$NLI_MODEL"
fi
if [ -n "$USER_INSTALL" ]; then
log "Running install-ce.sh --user (no sudo)..."
set -- "$@" --user
fi
# Pass only installer controls across sudo; do not forward the caller's
# unrelated environment. The model itself is pulled by the service user.
set -- env "SYNAPCORES_WITH_NLI=${SYNAPCORES_WITH_NLI:-}" \
"SYNAPCORES_NO_MODEL_DOWNLOAD=${SYNAPCORES_NO_MODEL_DOWNLOAD:-}" \
"SYNAPCORES_NONINTERACTIVE=${SYNAPCORES_NONINTERACTIVE:-}" "$@"
if [ -n "${AIDB_DATA_DIR:-}" ]; then
set -- env "AIDB_DATA_DIR=$AIDB_DATA_DIR" "$@"
fi
if [ -n "$USER_INSTALL" ] || [ "$(id -u)" -eq 0 ]; then
"$@"
else
log "System setup requires sudo..."
sudo "$@"
fi
else
warn "system installer not found at $INSTALLER_URL"
warn "binary is installed; complete setup manually per the docs"
fi
log "Done. Try: synapcores --version"