summaryrefslogtreecommitdiff
path: root/extra/install-kimi.sh
diff options
context:
space:
mode:
Diffstat (limited to 'extra/install-kimi.sh')
-rw-r--r--extra/install-kimi.sh185
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 "$@"