#!/bin/sh # crewkit CLI installer # Usage: curl -fsSL https://crewkit.io/install.sh | sh # curl -fsSL https://crewkit.io/install.sh | sh -s -- --version 0.2.0 # curl -fsSL https://crewkit.io/install.sh | sh -s -- --help set -e # Configuration GITHUB_REPO="karibew/crewkit-cli" INSTALL_DIR="${CREWKIT_INSTALL_DIR:-$HOME/.local/bin}" BINARY_NAME="crewkit" # Colors (if terminal supports it) if [ -t 1 ]; then RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color else RED='' GREEN='' YELLOW='' BLUE='' NC='' fi # Logging functions info() { printf "${BLUE}==>${NC} %s\n" "$1"; } success() { printf "${GREEN}==>${NC} %s\n" "$1"; } warn() { printf "${YELLOW}Warning:${NC} %s\n" "$1"; } error() { printf "${RED}Error:${NC} %s\n" "$1" >&2; exit 1; } # Parse arguments VERSION="" HELP=0 while [ $# -gt 0 ]; do case "$1" in --version|-v) VERSION="$2" shift 2 ;; --help|-h) HELP=1 shift ;; *) shift ;; esac done # Validate version format if provided if [ -n "$VERSION" ]; then if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$'; then error "Invalid version format: $VERSION (expected: X.Y.Z or X.Y.Z-prerelease)" fi fi if [ "$HELP" = "1" ]; then cat << 'HELPEOF' crewkit CLI Installer Manage your team's Claude Code agents with role-based configs, A/B testing, and accuracy monitoring. Usage: curl -fsSL https://crewkit.io/install.sh | sh curl -fsSL https://crewkit.io/install.sh | sh -s -- [options] Options: --version, -v Install a specific version (e.g., 0.2.0) --help, -h Show this help message Environment Variables: CREWKIT_INSTALL_DIR Custom installation directory (default: ~/.local/bin) Examples: # Install latest version curl -fsSL https://crewkit.io/install.sh | sh # Install specific version curl -fsSL https://crewkit.io/install.sh | sh -s -- --version 0.2.0 # Install to custom directory CREWKIT_INSTALL_DIR=/usr/local/bin curl -fsSL https://crewkit.io/install.sh | sh After Installation: crewkit auth login # Authenticate with your account crewkit sync # Sync agent configurations Learn more: https://crewkit.io/docs HELPEOF exit 0 fi # Detect platform and build Rust target triple detect_platform() { OS="$(uname -s)" ARCH="$(uname -m)" case "$OS" in Darwin) case "$ARCH" in x86_64) TARGET="x86_64-apple-darwin" ;; arm64) TARGET="aarch64-apple-darwin" ;; *) error "Unsupported macOS architecture: $ARCH" ;; esac ARCHIVE_EXT="tar.gz" ;; Linux) # Check for musl (Alpine, etc.) IS_MUSL=0 if ldd --version 2>&1 | grep -q musl; then IS_MUSL=1 fi case "$ARCH" in x86_64) if [ "$IS_MUSL" = "1" ]; then TARGET="x86_64-unknown-linux-musl" else TARGET="x86_64-unknown-linux-gnu" fi ;; aarch64|arm64) TARGET="aarch64-unknown-linux-gnu" ;; *) error "Unsupported Linux architecture: $ARCH" ;; esac ARCHIVE_EXT="tar.gz" ;; MINGW*|MSYS*|CYGWIN*) TARGET="x86_64-pc-windows-msvc" ARCHIVE_EXT="zip" ;; *) error "Unsupported operating system: $OS" ;; esac } # Check for required tools check_dependencies() { if command -v curl > /dev/null 2>&1; then DOWNLOADER="curl" elif command -v wget > /dev/null 2>&1; then DOWNLOADER="wget" else error "curl or wget is required but not found" fi if ! command -v tar > /dev/null 2>&1 && [ "$ARCHIVE_EXT" = "tar.gz" ]; then error "tar is required but not found" fi if ! command -v unzip > /dev/null 2>&1 && [ "$ARCHIVE_EXT" = "zip" ]; then error "unzip is required but not found" fi } # Download a file (HTTPS only) download() { url="$1" output="$2" # Enforce HTTPS case "$url" in https://*) ;; *) error "Only HTTPS URLs are supported" ;; esac if [ "$DOWNLOADER" = "curl" ]; then curl --proto '=https' --tlsv1.2 -fsSL "$url" -o "$output" else wget --https-only -q "$url" -O "$output" fi } # Get latest version from GitHub get_latest_version() { if [ -n "$VERSION" ]; then echo "$VERSION" return fi info "Fetching latest version..." >&2 if [ "$DOWNLOADER" = "curl" ]; then LATEST=$(curl --proto '=https' --tlsv1.2 -fsSL "https://api.github.com/repos/$GITHUB_REPO/releases/latest" | grep -m1 '"tag_name":' | head -1 | sed -E 's/.*"v([0-9]+\.[0-9]+\.[0-9]+[^"]*)".*/\1/') else LATEST=$(wget --https-only -qO- "https://api.github.com/repos/$GITHUB_REPO/releases/latest" | grep -m1 '"tag_name":' | head -1 | sed -E 's/.*"v([0-9]+\.[0-9]+\.[0-9]+[^"]*)".*/\1/') fi if [ -z "$LATEST" ]; then error "Could not determine latest version. Check your network or specify --version" fi # Validate extracted version format if ! echo "$LATEST" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+'; then error "Invalid version format from GitHub API: $LATEST" fi echo "$LATEST" } # Verify checksum verify_checksum() { archive="$1" expected_checksum="$2" if command -v sha256sum > /dev/null 2>&1; then actual=$(sha256sum "$archive" | cut -d' ' -f1) elif command -v shasum > /dev/null 2>&1; then actual=$(shasum -a 256 "$archive" | cut -d' ' -f1) elif command -v openssl > /dev/null 2>&1; then actual=$(openssl dgst -sha256 "$archive" | awk '{print $NF}') else error "No SHA256 tool found (sha256sum, shasum, or openssl required)" fi if [ "$actual" != "$expected_checksum" ]; then error "Checksum verification failed!\nExpected: $expected_checksum\nActual: $actual" fi success "Checksum verified" } # Main installation main() { info "crewkit CLI Installer" echo "" detect_platform info "Detected platform: $TARGET" check_dependencies VERSION=$(get_latest_version) info "Installing version: $VERSION" # Build download URLs ARCHIVE_NAME="crewkit-v${VERSION}-${TARGET}.${ARCHIVE_EXT}" DOWNLOAD_URL="https://github.com/$GITHUB_REPO/releases/download/v${VERSION}/${ARCHIVE_NAME}" CHECKSUMS_URL="https://github.com/$GITHUB_REPO/releases/download/v${VERSION}/SHA256SUMS" # Create temp directory with restricted permissions TMP_DIR=$(mktemp -d) chmod 700 "$TMP_DIR" trap 'rm -rf "$TMP_DIR"' EXIT # Download archive info "Downloading $ARCHIVE_NAME..." download "$DOWNLOAD_URL" "$TMP_DIR/$ARCHIVE_NAME" # Download and verify checksum info "Verifying checksum..." download "$CHECKSUMS_URL" "$TMP_DIR/SHA256SUMS" EXPECTED_CHECKSUM=$(grep "$ARCHIVE_NAME" "$TMP_DIR/SHA256SUMS" | cut -d' ' -f1) if [ -z "$EXPECTED_CHECKSUM" ]; then error "Checksum not found for $ARCHIVE_NAME - refusing to install unverified binary" fi verify_checksum "$TMP_DIR/$ARCHIVE_NAME" "$EXPECTED_CHECKSUM" # Extract archive info "Extracting..." cd "$TMP_DIR" if [ "$ARCHIVE_EXT" = "tar.gz" ]; then tar -xzf "$ARCHIVE_NAME" else unzip -q "$ARCHIVE_NAME" fi # Install binary info "Installing to $INSTALL_DIR..." mkdir -p "$INSTALL_DIR" if [ "$ARCHIVE_EXT" = "zip" ]; then mv "$BINARY_NAME.exe" "$INSTALL_DIR/" else mv "$BINARY_NAME" "$INSTALL_DIR/" chmod +x "$INSTALL_DIR/$BINARY_NAME" fi # Verify installation if [ -x "$INSTALL_DIR/$BINARY_NAME" ]; then success "crewkit $VERSION installed successfully!" else error "Installation failed" fi echo "" # Check if install dir is in PATH case ":$PATH:" in *":$INSTALL_DIR:"*) info "Run 'crewkit --help' to get started" ;; *) warn "$INSTALL_DIR is not in your PATH" echo "" echo "Add the following to your shell profile (~/.bashrc, ~/.zshrc, etc.):" echo "" echo ' export PATH="$HOME/.local/bin:$PATH"' echo "" echo "Then restart your shell or run:" echo "" echo " source ~/.bashrc # or ~/.zshrc" echo "" ;; esac echo "" echo "Documentation: https://crewkit.io/docs" echo "Get help: https://github.com/karibew/crewkit-cli/issues" } main