#!/usr/bin/env bash # claude-mode bootstrap installer for Linux and macOS. # # Served by the Arkylx Index and run as a one-liner: # # curl -fsSL https://index.arkylx.com/tools/claude-mode/install.sh | bash # # Downloads the payload, verifies its SHA-256 against the manifest, unpacks it to # a temp directory and runs the bundled install.sh. Nothing is written outside # ~/.claude-mode, ~/.local/bin and your shell rc. # # Environment overrides: # ARKYLX_CLAUDE_MODE_BASE base URL (default: index.arkylx.com) # ARKYLX_CLAUDE_MODE_VERSION pin a version instead of "latest" # ARKYLX_CODE enrolment code; reports the install to the Index set -euo pipefail BASE="${ARKYLX_CLAUDE_MODE_BASE:-https://index.arkylx.com/tools/claude-mode}" BASE="${BASE%/}" VERSION="${ARKYLX_CLAUDE_MODE_VERSION:-latest}" green() { printf ' \033[32mok \033[0m %s\n' "$*"; } warn() { printf ' \033[33mwarn\033[0m %s\n' "$*"; } fail() { printf ' \033[31mFAIL\033[0m %s\n' "$*" >&2; } printf '\n \033[36mclaude-mode installer\033[0m\n' printf ' \033[90msource: %s (%s)\033[0m\n\n' "$BASE" "$VERSION" # --- preflight ------------------------------------------------------------- need() { command -v "$1" >/dev/null 2>&1 || { fail "$1 is required but not installed"; exit 1; }; } need curl need tar PY="${CLAUDE_MODE_PYTHON:-python3}" command -v "$PY" >/dev/null 2>&1 || { fail "python3 is required (claude-mode uses it for JSON handling)" fail " Debian/Ubuntu: sudo apt install python3" fail " Fedora/RHEL: sudo dnf install python3" fail " macOS: xcode-select --install (or brew install python)" exit 1 } sha256_of() { if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1 elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | cut -d' ' -f1 else fail 'no sha256sum or shasum available - cannot verify the download'; exit 1 fi } WORK="$(mktemp -d 2>/dev/null || mktemp -d -t claude-mode)" cleanup() { rm -rf "$WORK"; } trap cleanup EXIT # --- manifest -------------------------------------------------------------- curl -fsSL --max-time 30 "$BASE/$VERSION/manifest.posix.json" -o "$WORK/manifest.json" || { fail "could not fetch $BASE/$VERSION/manifest.posix.json"; exit 1; } read_field() { "$PY" -c "import json,sys;print(json.load(open(sys.argv[1])).get(sys.argv[2],''))" "$WORK/manifest.json" "$1"; } PKG="$(read_field package)" EXPECTED="$(read_field sha256)" PKGVER="$(read_field version)" [ -n "$PKG" ] && [ -n "$EXPECTED" ] || { fail 'manifest is missing package/sha256'; exit 1; } # --- payload --------------------------------------------------------------- printf ' downloading %s (%s)\n' "$PKG" "$PKGVER" curl -fsSL --max-time 120 "$BASE/$VERSION/$PKG" -o "$WORK/$PKG" || { fail 'download failed'; exit 1; } ACTUAL="$(sha256_of "$WORK/$PKG")" if [ "$ACTUAL" != "$EXPECTED" ]; then fail 'checksum mismatch - refusing to install' fail "expected $EXPECTED" fail "actual $ACTUAL" exit 1 fi green 'checksum verified' mkdir -p "$WORK/src" tar -xzf "$WORK/$PKG" -C "$WORK/src" [ -f "$WORK/src/linux/install.sh" ] || { fail 'package does not contain linux/install.sh'; exit 1; } chmod +x "$WORK/src/linux/install.sh" printf '\n' "$WORK/src/linux/install.sh" "$@" # --- optional report ------------------------------------------------------- # Best-effort: a failure here must never make a good install look broken. if [ -n "${ARKYLX_CODE:-}" ] && [ "${ARKYLX_CLAUDE_MODE_REPORT:-1}" != "0" ]; then if curl -fsS --max-time 10 -X POST "$BASE/report" \ -H 'content-type: application/json' \ -d "{\"code\":\"$ARKYLX_CODE\",\"tool\":\"claude-mode\",\"version\":\"$PKGVER\",\"hostname\":\"$(hostname 2>/dev/null || echo unknown)\",\"os\":\"$(uname -sr 2>/dev/null || echo unknown)\"}" \ >/dev/null 2>&1; then green 'reported install to the Arkylx Index' else warn 'could not report to the Index (install is fine)' fi fi