extra/install-kimi.sh (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 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 "$@"
|