diff options
Diffstat (limited to 'extra/install-kimi.sh')
| -rw-r--r-- | extra/install-kimi.sh | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/extra/install-kimi.sh b/extra/install-kimi.sh new file mode 100644 index 0000000..d1013ca --- /dev/null +++ b/extra/install-kimi.sh @@ -0,0 +1,185 @@ +#!/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 "$@" |
