#!/usr/bin/env bash # # kimi-code installer for macOS and Linux. # # Usage: # curl -fsSL https://code.kimi.com/kimi-code/install.sh | bash # KIMI_VERSION=0.5.0 curl -fsSL https://code.kimi.com/kimi-code/install.sh | bash # KIMI_INSTALL_DIR=/usr/local curl -fsSL https://code.kimi.com/kimi-code/install.sh | sudo -E bash # # Optional env: # KIMI_VERSION Explicit version; if unset, fetch from https://code.kimi.com/kimi-code/latest # KIMI_INSTALL_DIR Installation directory, default $HOME/.kimi-code set -euo pipefail KIMI_DOWNLOAD_BASE="https://code.kimi.com/kimi-code" KIMI_VERSION="${KIMI_VERSION:-}" KIMI_INSTALL_DIR="${KIMI_INSTALL_DIR:-$HOME/.local/bin}" # ---------- helpers ---------- _have() { command -v "$1" >/dev/null 2>&1; } _log() { if [ -t 1 ]; then printf '\033[1;36m==>\033[0m %s\n' "$*" else printf '==> %s\n' "$*" fi } _err() { printf 'error: %s\n' "$*" >&2 exit 1 } _detect_target() { local os arch case "$(uname -s)" in Darwin) os="darwin" ;; Linux) os="linux" ;; MINGW*|MSYS*|CYGWIN*) _err "Windows is not supported by install.sh — use install.ps1 (PowerShell)" ;; *) _err "unsupported OS: $(uname -s)" ;; esac case "$(uname -m)" in x86_64|amd64) arch="x64" ;; arm64|aarch64) arch="arm64" ;; *) _err "unsupported architecture: $(uname -m)" ;; esac # Rosetta 2: when running under an x64 shell on an ARM Mac, switch back to the native arm64 binary if [ "$os" = "darwin" ] && [ "$arch" = "x64" ]; then if [ "$(sysctl -n sysctl.proc_translated 2>/dev/null)" = "1" ]; then arch="arm64" fi fi # musl detection (Alpine, etc.) — we currently only ship glibc binaries, so failing early is better than a runtime dlopen error if [ "$os" = "linux" ]; then if [ -f "/lib/libc.musl-x86_64.so.1" ] || \ [ -f "/lib/libc.musl-aarch64.so.1" ] || \ ldd /bin/ls 2>&1 | grep -q musl; then _err "Alpine / musl Linux is not currently supported. Use Node.js + 'npm install -g @moonshot-ai/kimi-code' instead." fi fi echo "${os}-${arch}" } _download() { local url="$1" dest="${2:-}" if _have curl; then if [ -n "$dest" ]; then if [ -t 1 ]; then curl --fail --location --progress-bar -o "$dest" "$url" else curl --fail --location --silent -o "$dest" "$url" fi else curl --fail --location --silent "$url" fi elif _have wget; then if [ -n "$dest" ]; then wget -q -O "$dest" "$url" else wget -q -O - "$url" fi else _err "curl or wget is required" fi } # Prefer jq; otherwise parse a single manifest field using pure bash regex _manifest_field() { local manifest_json="$1" target="$2" field="$3" if _have jq; then printf '%s' "$manifest_json" | jq -er ".platforms[\"$target\"].$field // empty" else local one_line one_line="$(printf '%s' "$manifest_json" | tr -d '\n\r\t' | sed 's/ \+/ /g')" if [[ $one_line =~ \"$target\"[^}]*\"$field\"[[:space:]]*:[[:space:]]*\"([^\"]+)\" ]]; then printf '%s' "${BASH_REMATCH[1]}" fi fi } _sha256_check() { local file="$1" expected="$2" local actual if _have shasum; then actual="$(shasum -a 256 "$file" | cut -d' ' -f1)" elif _have sha256sum; then actual="$(sha256sum "$file" | cut -d' ' -f1)" else _err "shasum or sha256sum required to verify download" fi if [ "$actual" != "$expected" ]; then _err "checksum mismatch: expected $expected, got $actual" fi } # ---------- main ---------- TMPDIR_INSTALL="" _cleanup() { if [ -n "$TMPDIR_INSTALL" ] && [ -d "$TMPDIR_INSTALL" ]; then rm -rf "$TMPDIR_INSTALL" fi } trap _cleanup EXIT main() { local target version manifest_url manifest filename checksum binary_url tmpdir target="$(_detect_target)" _log "Detected target: $target" # 1. Resolve version if [ -n "$KIMI_VERSION" ]; then version="$KIMI_VERSION" _log "Using pinned version $version" else _log "Resolving latest version from ${KIMI_DOWNLOAD_BASE}/latest" version="$(_download "${KIMI_DOWNLOAD_BASE}/latest" | tr -d '[:space:]')" [ -n "$version" ] || _err "could not resolve latest version" _log "Latest version: $version" fi # 2. Fetch manifest manifest_url="${KIMI_DOWNLOAD_BASE}/${version}/manifest.json" _log "Fetching manifest ${manifest_url}" manifest="$(_download "$manifest_url")" [ -n "$manifest" ] || _err "manifest is empty or unreachable: $manifest_url" # 3. Find current platform entry filename="$(_manifest_field "$manifest" "$target" "filename")" checksum="$(_manifest_field "$manifest" "$target" "checksum")" [ -n "$filename" ] || _err "platform $target not found in manifest" [[ "$checksum" =~ ^[a-f0-9]{64}$ ]] || _err "invalid checksum for $target: $checksum" # 4. Download binary TMPDIR_INSTALL="$(mktemp -d 2>/dev/null || mktemp -d -t kimi-install)" tmpdir="$TMPDIR_INSTALL" binary_url="${KIMI_DOWNLOAD_BASE}/${version}/${filename}" _log "Downloading ${binary_url}" _download "$binary_url" "${tmpdir}/${filename}" # 5. Verify checksum _log "Verifying checksum" _sha256_check "${tmpdir}/${filename}" "$checksum" # 6. Install chmod +x "${tmpdir}/${filename}" mkdir -p "${KIMI_INSTALL_DIR}" install -m 0755 "${tmpdir}/${filename}" "${KIMI_INSTALL_DIR}/kimi" _log "Installed to ${KIMI_INSTALL_DIR}/kimi" _log "Done. Run: kimi --version" } main "$@"