Files
wehub-resource-sync 26382a7ac6
CI / Test (macos-latest) (push) Waiting to run
CI / Test (windows-latest) (push) Waiting to run
CI / Build (no embeddings / no ORT) (push) Waiting to run
CI / Format (push) Waiting to run
CI / Cookbook (Node) (push) Waiting to run
CI / Pi Extension (Node) (push) Waiting to run
CI / Rust SDK (lean-ctx-client) (push) Waiting to run
CI / Embed SDK (lean-ctx-sdk) (push) Waiting to run
CI / Python SDK (leanctx) (push) Waiting to run
CI / Hermes Plugin (Python) (push) Waiting to run
CI / SDK Conformance Matrix (push) Waiting to run
CI / Coverage (push) Waiting to run
CI / cargo-deny (push) Waiting to run
CI / Adversarial Safety (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
JetBrains Plugin / Actionlint (push) Waiting to run
CI / Benchmarks (push) Waiting to run
CI / Output-Quality Gate (eval A/B) (push) Waiting to run
CI / Documentation (push) Waiting to run
CI / CI Green (push) Blocked by required conditions
CodeQL / Analyze (actions) (push) Waiting to run
CodeQL / Analyze (rust) (push) Waiting to run
JetBrains Plugin / Validation (push) Waiting to run
JetBrains Plugin / Build (push) Waiting to run
JetBrains Plugin / Test (push) Blocked by required conditions
Security Check / Security Scan (push) Waiting to run
CI / Test (ubuntu-latest) (push) Has started running
CI / Clippy (push) Has started running
chore: import upstream snapshot with attribution
2026-07-13 12:35:30 +08:00

369 lines
12 KiB
Bash
Executable File

#!/bin/sh
# install.sh — Install lean-ctx (download pre-built binary or build from source)
#
# Usage:
# ./install.sh # download pre-built binary (no Rust needed)
# ./install.sh --download # download pre-built binary (no Rust needed)
# ./install.sh --cuda # download Linux x86_64 CUDA-enabled binary
# ./install.sh --build-only # build only, don't install
# ./install.sh --uninstall # fully remove lean-ctx (processes, configs, autostart, data, binary)
#
# One-liner (no Rust required):
# curl -fsSL https://leanctx.com/install.sh | sh
# curl -fsSL https://leanctx.com/install.sh | bash
#
# Uninstall one-liner:
# curl -fsSL https://leanctx.com/install.sh | sh -s -- --uninstall
set -eu
REPO="yvgude/lean-ctx"
INSTALL_DIR="${LEAN_CTX_INSTALL_DIR:-$HOME/.local/bin}"
INSTALL_FLAVOR="${LEAN_CTX_INSTALL_FLAVOR:-cpu}"
# Resolve the script's directory when invoked as a file. When piped via
# `curl ... | sh`, $0 is "sh" (or similar) — the [ -f "$0" ] guard then
# falls back to pwd, which is what the bottom-of-file dispatcher expects:
# RUST_DIR check fails outside the repo, so we route to install_download.
SCRIPT_DIR="$(
src="$0"
if [ -n "$src" ] && [ -f "$src" ]; then
cd "$(dirname "$src")" 2>/dev/null && pwd
else
pwd
fi
)"
RUST_DIR="$SCRIPT_DIR/rust"
echo "lean-ctx installer"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
finish() {
# --- Auto-fix PATH if needed ---
case ":$PATH:" in
*":$INSTALL_DIR:"*) ;;
*)
echo ""
shell_name="$(basename "${SHELL:-bash}" 2>/dev/null || echo bash)"
rc="$HOME/.bashrc"
case "$shell_name" in
zsh) rc="$HOME/.zshrc" ;;
fish) rc="$HOME/.config/fish/config.fish" ;;
esac
if [ "${LEAN_CTX_NO_PATH_FIX:-}" = "1" ]; then
echo "Warning: $INSTALL_DIR is not in your PATH."
echo " Add it manually, then run: lean-ctx onboard"
else
echo "Adding $INSTALL_DIR to PATH..."
if [ "$shell_name" = "fish" ]; then
printf '\nfish_add_path %s\n' "$INSTALL_DIR" >> "$rc" 2>/dev/null || true
else
printf '\nexport PATH="%s:$PATH"\n' "$INSTALL_DIR" >> "$rc" 2>/dev/null || true
if [ "$shell_name" = "bash" ] && [ "$(uname -s)" = "Darwin" ]; then
grep -qs '.bashrc' "$HOME/.bash_profile" 2>/dev/null || \
printf '\n[ -f ~/.bashrc ] && . ~/.bashrc\n' >> "$HOME/.bash_profile" 2>/dev/null || true
fi
fi
export PATH="$INSTALL_DIR:$PATH"
echo " Done. PATH updated in $rc and current session."
fi
;;
esac
echo ""
echo "Done! lean-ctx $(\"$INSTALL_DIR/lean-ctx\" --version 2>/dev/null || echo 'installed')."
# --- Auto-onboard unless opted out ---
if [ "${LEAN_CTX_NO_ONBOARD:-}" = "1" ]; then
echo ""
echo "Next step: Run 'lean-ctx onboard' to connect your AI tools."
return
fi
echo ""
echo "Running onboard (connecting your AI tools)..."
"$INSTALL_DIR/lean-ctx" onboard 2>&1 || true
# --- Detect installed agents and suggest wrap ---
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Setup complete! Quick start:"
echo ""
echo " lean-ctx wrap cursor # one-command setup for Cursor"
echo " lean-ctx wrap claude # one-command setup for Claude Code"
echo " lean-ctx wrap codex # one-command setup for Codex CLI"
echo ""
echo " lean-ctx doctor # verify installation"
echo " lean-ctx gain # see savings after first use"
echo ""
echo "Full control: lean-ctx setup (interactive wizard)"
echo "Skip auto-onboard: curl ... | LEAN_CTX_NO_ONBOARD=1 sh"
}
detect_target() {
os="$(uname -s | tr '[:upper:]' '[:lower:]')"
arch="$(uname -m)"
case "$arch" in
x86_64) arch="x86_64" ;;
arm64|aarch64) arch="aarch64" ;;
*)
echo "Error: unsupported architecture '$arch'"
echo "Build from source instead: ./install.sh"
exit 1 ;;
esac
case "$os" in
linux)
libc="musl"
if command -v ldd >/dev/null 2>&1; then
glibc_ver="$(ldd --version 2>&1 | head -1 | grep -oE '[0-9]+\.[0-9]+$' || true)"
if [ -n "$glibc_ver" ]; then
major="${glibc_ver%%.*}"
minor="${glibc_ver##*.}"
if [ "$major" -gt 2 ] || { [ "$major" -eq 2 ] && [ "$minor" -ge 35 ]; }; then
libc="gnu"
fi
fi
fi
echo "${arch}-unknown-linux-${libc}"
;;
darwin) echo "${arch}-apple-darwin" ;;
*)
echo "Error: unsupported OS '$os'"
echo "Windows: download from https://github.com/${REPO}/releases/latest"
exit 1 ;;
esac
}
verify_checksum() {
file="$1"
expected="$2"
if command -v sha256sum >/dev/null 2>&1; then
actual="$(sha256sum "$file" | cut -d' ' -f1)"
elif command -v shasum >/dev/null 2>&1; then
actual="$(shasum -a 256 "$file" | cut -d' ' -f1)"
else
echo "Warning: no sha256sum/shasum found, skipping checksum verification"
return 0
fi
if [ "$actual" != "$expected" ]; then
echo "Error: checksum mismatch!"
echo " Expected: $expected"
echo " Got: $actual"
exit 1
fi
echo " Checksum verified ✓"
}
# Stop any running lean-ctx before swapping the binary. The proxy runs as a
# LaunchAgent/systemd unit with KeepAlive, so without this it keeps a stale
# binary alive (and may respawn mid-swap); `lean-ctx stop` boots it out so the
# freshly installed binary is picked up cleanly on next use. No-op on a first
# install (nothing on PATH yet). Best-effort — never fails the install.
stop_running_instance() {
if command -v lean-ctx >/dev/null 2>&1; then
echo "Stopping running lean-ctx (if any)..."
lean-ctx stop >/dev/null 2>&1 || true
fi
}
install_download() {
target="$(detect_target)"
case "$INSTALL_FLAVOR" in
cuda|gpu)
if [ "$target" != "x86_64-unknown-linux-gnu" ]; then
echo "Error: CUDA pre-built binary is currently published for x86_64 GNU/Linux only."
echo "Detected: $target"
exit 1
fi
target="${target}-cuda"
;;
cpu|"") ;;
*)
echo "Error: unknown install flavor '$INSTALL_FLAVOR' (expected cpu or cuda)"
exit 1
;;
esac
echo "Mode: download pre-built binary"
echo "Platform: $target"
echo ""
echo "Fetching latest release..."
latest="$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" \
| grep '"tag_name"' | head -1 | cut -d'"' -f4)"
if [ -z "$latest" ]; then
echo "Error: could not determine latest release."
exit 1
fi
echo "Latest: $latest"
asset_url="https://github.com/${REPO}/releases/download/${latest}/lean-ctx-${target}.tar.gz"
sums_url="https://github.com/${REPO}/releases/download/${latest}/SHA256SUMS"
tmpdir="$(mktemp -d)"
tmp_bin=""
trap 'rm -rf "${tmpdir:-}"; [ -n "${tmp_bin:-}" ] && rm -f "${tmp_bin:-}" 2>/dev/null || true' EXIT
echo "Downloading binary..."
if ! curl -fsSL "$asset_url" -o "$tmpdir/lean-ctx.tar.gz"; then
echo "Error: download failed. Check: https://github.com/${REPO}/releases"
exit 1
fi
echo "Downloading checksums..."
if curl -fsSL "$sums_url" -o "$tmpdir/SHA256SUMS" 2>/dev/null; then
expected="$(grep "lean-ctx-${target}.tar.gz" "$tmpdir/SHA256SUMS" | cut -d' ' -f1)"
if [ -n "$expected" ]; then
verify_checksum "$tmpdir/lean-ctx.tar.gz" "$expected"
fi
else
echo " Warning: checksums not available, skipping verification"
fi
tar -xzf "$tmpdir/lean-ctx.tar.gz" -C "$tmpdir"
mkdir -p "$INSTALL_DIR"
tmp_bin="$INSTALL_DIR/.lean-ctx.new.$$"
install -m755 "$tmpdir/lean-ctx" "$tmp_bin"
if [ "$(uname -s)" = "Darwin" ]; then
xattr -cr "$tmp_bin" 2>/dev/null || true
codesign --force --sign - "$tmp_bin" 2>/dev/null || true
fi
stop_running_instance
mv -f "$tmp_bin" "$INSTALL_DIR/lean-ctx"
tmp_bin=""
echo " Installed: $INSTALL_DIR/lean-ctx"
finish
}
install_from_source() {
if ! command -v cargo >/dev/null 2>&1; then
echo "Error: cargo not found. Install Rust: https://rustup.rs"
echo "Or download a pre-built binary: $0 --download"
exit 1
fi
build_only="${1:-}"
echo "Mode: build from source"
echo ""
echo "Building lean-ctx (release)..."
if [ -d "$RUST_DIR" ]; then
(cd "$RUST_DIR" && cargo build --release)
# Honour CARGO_TARGET_DIR / a config.toml [build] target-dir override
# (GH #671): with a hardcoded ./target, a stale binary from an earlier
# default-layout build gets linked instead of the one just built.
# `|| true` keeps a failing `cargo metadata` on the fallback path.
target_dir=$( (cd "$RUST_DIR" && cargo metadata --no-deps --format-version=1 2>/dev/null) \
| grep -o '"target_directory":"[^"]*"' \
| head -1 \
| sed -E 's/^"target_directory":"(.*)"$/\1/' \
| sed 's/\\\\/\//g' || true)
binary="${target_dir:-$RUST_DIR/target}/release/lean-ctx"
else
cargo install lean-ctx
echo ""
echo "Installed via cargo install."
return
fi
if [ ! -x "$binary" ]; then
echo "Error: build failed — binary not found at $binary"
echo "Hint: is CARGO_TARGET_DIR or ~/.cargo/config.toml [build] target-dir pointing elsewhere?"
exit 1
fi
echo "Built: $binary"
if [ "$build_only" = "--build-only" ]; then
echo "Done (build only)."
return
fi
mkdir -p "$INSTALL_DIR"
tmp_link="$INSTALL_DIR/.lean-ctx.link.$$"
ln -sf "$binary" "$tmp_link"
stop_running_instance
mv -f "$tmp_link" "$INSTALL_DIR/lean-ctx"
echo " Linked: $INSTALL_DIR/lean-ctx -> $binary"
finish
}
uninstall() {
echo "Mode: uninstall"
echo ""
# The binary's own `uninstall` does the thorough cleanup — stops every process, then
# removes hooks, MCP configs, rules, autostart (LaunchAgent/systemd), data, and the
# binary itself. Prefer it; forward any extra flags (e.g. --keep-config, --dry-run).
if command -v lean-ctx >/dev/null 2>&1; then
lean-ctx uninstall "$@" || true
else
echo "lean-ctx not on PATH — removing known artifacts directly."
if [ "$(uname -s)" = "Darwin" ]; then
for label in com.leanctx.proxy com.leanctx.daemon; do
plist="$HOME/Library/LaunchAgents/$label.plist"
[ -f "$plist" ] && launchctl unload "$plist" 2>/dev/null || true
rm -f "$plist" 2>/dev/null || true
done
else
for svc in lean-ctx-proxy lean-ctx-daemon; do
systemctl --user disable --now "$svc" 2>/dev/null || true
rm -f "$HOME/.config/systemd/user/$svc.service" 2>/dev/null || true
done
systemctl --user daemon-reload 2>/dev/null || true
fi
rm -rf "$HOME/.lean-ctx" "$HOME/.config/lean-ctx" 2>/dev/null || true
echo " Removed autostart + data dir."
echo " (Reinstall the binary and run 'lean-ctx uninstall' for full editor-config cleanup.)"
fi
# Belt-and-suspenders: ensure the binary + PATH symlinks install.sh created are gone,
# even if the self-delete failed or the binary was never on PATH.
for b in "$INSTALL_DIR/lean-ctx" "/usr/local/bin/lean-ctx"; do
if [ -e "$b" ] || [ -L "$b" ]; then
rm -f "$b" 2>/dev/null && echo " Removed $b" || true
fi
done
echo ""
echo "lean-ctx uninstalled. Restart your shell to drop stale aliases."
echo "Verify with: command -v lean-ctx # should print nothing"
}
case "${1:-}" in
--download) install_download ;;
--cuda|--gpu) INSTALL_FLAVOR="cuda"; install_download ;;
--build-only) install_from_source --build-only ;;
--uninstall) shift; uninstall "$@" ;;
--help|-h)
echo "Usage: $0 [--download|--cuda|--build-only|--uninstall|--help]"
echo ""
echo " (no args) Download pre-built binary (builds from source if run inside the lean-ctx repo)"
echo " --download Download pre-built binary (no Rust needed)"
echo " --cuda Download Linux x86_64 CUDA-enabled binary"
echo " --build-only Build only, don't install"
echo " --uninstall Fully remove lean-ctx (processes, configs, autostart, data, binary)"
echo ""
echo "Uninstall one-liner:"
echo " curl -fsSL https://leanctx.com/install.sh | sh -s -- --uninstall"
echo ""
echo "Environment:"
echo " LEAN_CTX_INSTALL_DIR Custom install directory (default: ~/.local/bin)"
echo " LEAN_CTX_INSTALL_FLAVOR Binary flavor: cpu or cuda (default: cpu)"
;;
*)
if [ -d "$RUST_DIR" ]; then
install_from_source
else
install_download
fi
;;
esac