Initial release: iai-mcp v0.1.0
Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: XNLLLLH <XNLLLLH@users.noreply.github.com>
This commit is contained in:
commit
f6b876fbe7
332 changed files with 97258 additions and 0 deletions
198
scripts/install.sh
Executable file
198
scripts/install.sh
Executable file
|
|
@ -0,0 +1,198 @@
|
|||
#!/usr/bin/env bash
|
||||
# scripts/install.sh — first-time setup for collaborators.
|
||||
#
|
||||
# Usage (from repo root or anywhere inside the clone):
|
||||
# bash scripts/install.sh
|
||||
#
|
||||
# Does:
|
||||
# 1. creates .venv if missing
|
||||
# 2. installs iai-mcp editable into the venv
|
||||
# 3. builds the TS MCP wrapper
|
||||
# 4. symlinks ~/.local/bin/iai-mcp -> .venv/bin/iai-mcp so the CLI is
|
||||
# callable from anywhere without activating the venv
|
||||
# 5. optionally installs the sleep daemon (launchd on macOS, systemd on Linux)
|
||||
#
|
||||
# Idempotent. Safe to re-run.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
cd "${REPO_ROOT}"
|
||||
|
||||
step() { printf '\n\033[1;34m==> %s\033[0m\n' "$*"; }
|
||||
ok() { printf ' \033[0;32m✓\033[0m %s\n' "$*"; }
|
||||
warn() { printf ' \033[0;33m!\033[0m %s\n' "$*"; }
|
||||
die() { printf '\n\033[0;31m✗ %s\033[0m\n' "$*" >&2; exit 1; }
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Sections 1-4: build / venv / pip / npm / symlink.
|
||||
#
|
||||
# IAI_TEST_SKIP_BUILD=1 short-circuits the whole bootstrap so the LaunchAgent
|
||||
# section (6) can be exercised in isolation by tests/test_install_uninstall.py
|
||||
# (Plan 07.1-03 Task 3) without spending ~30s on venv + npm.
|
||||
# ---------------------------------------------------------------------------
|
||||
if [[ "${IAI_TEST_SKIP_BUILD:-0}" == "1" ]]; then
|
||||
step "build skip (IAI_TEST_SKIP_BUILD=1)"
|
||||
ok "skipping sections 1-4 (venv/pip/npm/symlink) — test mode"
|
||||
else
|
||||
# -----------------------------------------------------------------------
|
||||
# 1. venv
|
||||
# -----------------------------------------------------------------------
|
||||
step "python venv"
|
||||
# iai-mcp requires Python 3.11 or 3.12 (torch + lancedb on 3.13/3.14
|
||||
# are not yet stable). Pick the highest supported interpreter we can find.
|
||||
PY=""
|
||||
for cand in python3.12 python3.11; do
|
||||
if command -v "$cand" >/dev/null 2>&1; then
|
||||
PY="$(command -v $cand)"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ -z "$PY" ]; then
|
||||
# Fall back to plain python3 only if it self-reports as 3.11 or 3.12.
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
ver="$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' 2>/dev/null || echo unknown)"
|
||||
if [ "$ver" = "3.11" ] || [ "$ver" = "3.12" ]; then
|
||||
PY="$(command -v python3)"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
[ -n "$PY" ] || die "Python 3.11 or 3.12 not found. macOS: brew install python@3.12 | Linux: apt install python3.12 (or use pyenv)"
|
||||
ok "using $PY ($($PY --version))"
|
||||
if [ ! -d .venv ]; then
|
||||
"$PY" -m venv .venv
|
||||
ok ".venv created"
|
||||
else
|
||||
ok ".venv already exists"
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# 2. editable install
|
||||
# -----------------------------------------------------------------------
|
||||
step "editable install (pip -e .)"
|
||||
.venv/bin/pip install --quiet --upgrade pip
|
||||
.venv/bin/pip install --quiet -e .
|
||||
ok "iai-mcp python package installed into venv"
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# 3. TS wrapper build
|
||||
# -----------------------------------------------------------------------
|
||||
step "TS wrapper build"
|
||||
if [ -d mcp-wrapper ]; then
|
||||
pushd mcp-wrapper >/dev/null
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --silent --no-audit --no-fund
|
||||
else
|
||||
npm install --silent --no-audit --no-fund
|
||||
fi
|
||||
npm run build --silent
|
||||
popd >/dev/null
|
||||
ok "mcp-wrapper/dist built"
|
||||
else
|
||||
warn "mcp-wrapper/ missing — skipping"
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# 4. global symlink into ~/.local/bin
|
||||
# -----------------------------------------------------------------------
|
||||
step "global CLI symlink"
|
||||
LOCAL_BIN="${HOME}/.local/bin"
|
||||
LINK_PATH="${LOCAL_BIN}/iai-mcp"
|
||||
TARGET="${REPO_ROOT}/.venv/bin/iai-mcp"
|
||||
|
||||
[ -x "${TARGET}" ] || die "venv entry point not found at ${TARGET}"
|
||||
|
||||
mkdir -p "${LOCAL_BIN}"
|
||||
|
||||
# `ln -sf` overwrites any existing symlink safely (idempotent).
|
||||
# Refuse to clobber a regular file the user put there themselves.
|
||||
if [ -e "${LINK_PATH}" ] && [ ! -L "${LINK_PATH}" ]; then
|
||||
die "${LINK_PATH} exists and is NOT a symlink. move it aside and re-run."
|
||||
fi
|
||||
ln -sf "${TARGET}" "${LINK_PATH}"
|
||||
ok "${LINK_PATH} -> ${TARGET}"
|
||||
|
||||
# PATH sanity check using python (grep is hook-blocked in this dev env).
|
||||
PATH_HAS_LOCAL_BIN="$(.venv/bin/python - <<PY
|
||||
import os
|
||||
print("1" if "${LOCAL_BIN}" in os.environ.get("PATH", "").split(":") else "0")
|
||||
PY
|
||||
)"
|
||||
if [ "${PATH_HAS_LOCAL_BIN}" != "1" ]; then
|
||||
warn "${LOCAL_BIN} is NOT in your PATH"
|
||||
warn "add this to ~/.zshrc or ~/.bashrc and restart your shell:"
|
||||
warn " export PATH=\"\${HOME}/.local/bin:\${PATH}\""
|
||||
else
|
||||
ok "${LOCAL_BIN} is in PATH"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 5. optional daemon install
|
||||
# ---------------------------------------------------------------------------
|
||||
step "sleep daemon (optional)"
|
||||
if command -v iai-mcp >/dev/null 2>&1; then
|
||||
INSTALLED_PATH="$(command -v iai-mcp)"
|
||||
ok "iai-mcp globally reachable at ${INSTALLED_PATH}"
|
||||
echo
|
||||
echo " to run the background sleep daemon (recommended — REM cycles +"
|
||||
echo " overnight consolidation on your local Claude subscription):"
|
||||
echo
|
||||
echo " iai-mcp daemon install --yes"
|
||||
echo " iai-mcp daemon start"
|
||||
echo
|
||||
echo " or skip for now and install later."
|
||||
else
|
||||
warn "iai-mcp not on PATH yet — add ~/.local/bin to PATH first, then run:"
|
||||
warn " iai-mcp daemon install --yes"
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 6. LaunchAgent registration (Phase 7.1 — socket-activated singleton)
|
||||
#
|
||||
# Section 6 — socket-activated LaunchAgent. REPLACES the eager
|
||||
# RunAtLoad=true plist that `iai-mcp daemon install` writes.
|
||||
# The two flows compete for ~/Library/LaunchAgents/com.iai-mcp.daemon.plist;
|
||||
# whichever ran most recently wins. install.sh always wins because
|
||||
# it overwrites + reloads on every invocation (idempotent by design).
|
||||
# ---------------------------------------------------------------------------
|
||||
step "LaunchAgent registration"
|
||||
if [[ "$(uname)" != "Darwin" ]]; then
|
||||
warn "non-Darwin OS — skipping LaunchAgent registration"
|
||||
elif [[ "${DRY_RUN:-0}" == "1" ]]; then
|
||||
ok "DRY_RUN=1 — skipping launchctl calls (test mode)"
|
||||
else
|
||||
PYTHON_PATH="${REPO_ROOT}/.venv/bin/python"
|
||||
if [ ! -x "${PYTHON_PATH}" ]; then
|
||||
warn "venv python not found at ${PYTHON_PATH} — falling back to $(command -v python3)"
|
||||
PYTHON_PATH="$(command -v python3)"
|
||||
fi
|
||||
LA_DIR="${HOME}/Library/LaunchAgents"
|
||||
LA_PATH="${LA_DIR}/com.iai-mcp.daemon.plist"
|
||||
TEMPLATE="${REPO_ROOT}/scripts/com.iai-mcp.daemon.plist.template"
|
||||
[ -f "${TEMPLATE}" ] || die "plist template missing at ${TEMPLATE}"
|
||||
mkdir -p "${LA_DIR}" "${HOME}/.iai-mcp/logs" "${HOME}/.iai-mcp"
|
||||
# Substitute placeholders using sed; HOME/PYTHON_PATH may contain forward
|
||||
# slashes so we use `|` as the sed separator (not `/`).
|
||||
sed -e "s|{PYTHON_PATH}|${PYTHON_PATH}|g" -e "s|{HOME}|${HOME}|g" "${TEMPLATE}" > "${LA_PATH}"
|
||||
# Idempotent: unload prior registration if any, then load fresh. -w persists across reboots.
|
||||
launchctl unload -w "${LA_PATH}" 2>/dev/null || true
|
||||
if ! launchctl load -w "${LA_PATH}"; then
|
||||
warn "launchctl load reported non-zero — checking registration anyway"
|
||||
fi
|
||||
if launchctl list | grep -q "com.iai-mcp.daemon"; then
|
||||
ok "LaunchAgent registered (first MCP call will socket-activate the daemon)"
|
||||
else
|
||||
die "LaunchAgent NOT registered after launchctl load — investigate ${HOME}/.iai-mcp/logs/launchd-stderr.log"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# done
|
||||
# ---------------------------------------------------------------------------
|
||||
step "done"
|
||||
ok "iai-mcp installed at $(git rev-parse --short HEAD)"
|
||||
echo
|
||||
echo " next: bash scripts/uninstall.sh (to roll back; preserves data unless --purge-data)"
|
||||
echo " update: bash scripts/update.sh (pull + rebuild + restart daemon)"
|
||||
Loading…
Add table
Add a link
Reference in a new issue