install 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. #!/usr/bin/env bash
  2. set -euo pipefail
  3. APP=kilo # kilocode_change
  4. MUTED='\033[0;2m'
  5. RED='\033[0;31m'
  6. ORANGE='\033[38;5;214m'
  7. NC='\033[0m' # No Color
  8. # kilocode_change start - usage text
  9. usage() {
  10. cat <<EOF
  11. Kilo Code Installer
  12. Usage: install [options]
  13. Options:
  14. -h, --help Display this help message
  15. -v, --version <version> Install a specific version (e.g., 1.0.180)
  16. -b, --binary <path> Install from a local binary instead of downloading
  17. --no-modify-path Don't modify shell config files (.zshrc, .bashrc, etc.)
  18. Examples:
  19. curl -fsSL https://kilo.ai/cli/install | bash
  20. curl -fsSL https://kilo.ai/cli/install | bash -s -- --version 1.0.180
  21. ./install --binary /path/to/kilo
  22. EOF
  23. }
  24. # kilocode_change end
  25. requested_version=${VERSION:-}
  26. no_modify_path=false
  27. binary_path=""
  28. while [[ $# -gt 0 ]]; do
  29. case "$1" in
  30. -h|--help)
  31. usage
  32. exit 0
  33. ;;
  34. -v|--version)
  35. if [[ -n "${2:-}" ]]; then
  36. requested_version="$2"
  37. shift 2
  38. else
  39. echo -e "${RED}Error: --version requires a version argument${NC}"
  40. exit 1
  41. fi
  42. ;;
  43. -b|--binary)
  44. if [[ -n "${2:-}" ]]; then
  45. binary_path="$2"
  46. shift 2
  47. else
  48. echo -e "${RED}Error: --binary requires a path argument${NC}"
  49. exit 1
  50. fi
  51. ;;
  52. --no-modify-path)
  53. no_modify_path=true
  54. shift
  55. ;;
  56. *)
  57. echo -e "${ORANGE}Warning: Unknown option '$1'${NC}" >&2
  58. shift
  59. ;;
  60. esac
  61. done
  62. INSTALL_DIR=$HOME/.kilo/bin # kilocode_change
  63. mkdir -p "$INSTALL_DIR"
  64. # If --binary is provided, skip all download/detection logic
  65. if [ -n "$binary_path" ]; then
  66. if [ ! -f "$binary_path" ]; then
  67. echo -e "${RED}Error: Binary not found at ${binary_path}${NC}"
  68. exit 1
  69. fi
  70. specific_version="local"
  71. else
  72. raw_os=$(uname -s)
  73. os=$(echo "$raw_os" | tr '[:upper:]' '[:lower:]')
  74. case "$raw_os" in
  75. Darwin*) os="darwin" ;;
  76. Linux*) os="linux" ;;
  77. MINGW*|MSYS*|CYGWIN*) os="windows" ;;
  78. esac
  79. arch=$(uname -m)
  80. if [[ "$arch" == "aarch64" ]]; then
  81. arch="arm64"
  82. fi
  83. if [[ "$arch" == "x86_64" ]]; then
  84. arch="x64"
  85. fi
  86. if [ "$os" = "darwin" ] && [ "$arch" = "x64" ]; then
  87. rosetta_flag=$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)
  88. if [ "$rosetta_flag" = "1" ]; then
  89. arch="arm64"
  90. fi
  91. fi
  92. combo="$os-$arch"
  93. case "$combo" in
  94. linux-x64|linux-arm64|darwin-x64|darwin-arm64|windows-x64)
  95. ;;
  96. *)
  97. echo -e "${RED}Unsupported OS/Arch: $os/$arch${NC}"
  98. exit 1
  99. ;;
  100. esac
  101. archive_ext=".zip"
  102. if [ "$os" = "linux" ]; then
  103. archive_ext=".tar.gz"
  104. fi
  105. is_musl=false
  106. if [ "$os" = "linux" ]; then
  107. if [ -f /etc/alpine-release ]; then
  108. is_musl=true
  109. fi
  110. if command -v ldd >/dev/null 2>&1; then
  111. if ldd --version 2>&1 | grep -qi musl; then
  112. is_musl=true
  113. fi
  114. fi
  115. fi
  116. needs_baseline=false
  117. if [ "$arch" = "x64" ]; then
  118. if [ "$os" = "linux" ]; then
  119. if ! grep -qwi avx2 /proc/cpuinfo 2>/dev/null; then
  120. needs_baseline=true
  121. fi
  122. fi
  123. if [ "$os" = "darwin" ]; then
  124. avx2=$(sysctl -n hw.optional.avx2_0 2>/dev/null || echo 0)
  125. if [ "$avx2" != "1" ]; then
  126. needs_baseline=true
  127. fi
  128. fi
  129. if [ "$os" = "windows" ]; then
  130. ps="(Add-Type -MemberDefinition \"[DllImport(\"\"kernel32.dll\"\")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);\" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)"
  131. out=""
  132. if command -v powershell.exe >/dev/null 2>&1; then
  133. out=$(powershell.exe -NoProfile -NonInteractive -Command "$ps" 2>/dev/null || true)
  134. elif command -v pwsh >/dev/null 2>&1; then
  135. out=$(pwsh -NoProfile -NonInteractive -Command "$ps" 2>/dev/null || true)
  136. fi
  137. out=$(echo "$out" | tr -d '\r' | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')
  138. if [ "$out" != "true" ] && [ "$out" != "1" ]; then
  139. needs_baseline=true
  140. fi
  141. fi
  142. fi
  143. target="$os-$arch"
  144. if [ "$needs_baseline" = "true" ]; then
  145. target="$target-baseline"
  146. fi
  147. if [ "$is_musl" = "true" ]; then
  148. target="$target-musl"
  149. fi
  150. filename="$APP-$target$archive_ext"
  151. if [ "$os" = "linux" ]; then
  152. if ! command -v tar >/dev/null 2>&1; then
  153. echo -e "${RED}Error: 'tar' is required but not installed.${NC}"
  154. exit 1
  155. fi
  156. else
  157. if ! command -v unzip >/dev/null 2>&1; then
  158. echo -e "${RED}Error: 'unzip' is required but not installed.${NC}"
  159. exit 1
  160. fi
  161. fi
  162. if [ -z "$requested_version" ]; then
  163. url="https://github.com/Kilo-Org/kilocode/releases/latest/download/$filename"
  164. specific_version=$(curl -s https://api.github.com/repos/Kilo-Org/kilocode/releases/latest | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
  165. if [[ $? -ne 0 || -z "$specific_version" ]]; then
  166. echo -e "${RED}Failed to fetch version information${NC}"
  167. exit 1
  168. fi
  169. else
  170. # Strip leading 'v' if present
  171. requested_version="${requested_version#v}"
  172. url="https://github.com/Kilo-Org/kilocode/releases/download/v${requested_version}/$filename"
  173. specific_version=$requested_version
  174. # Verify the release exists before downloading
  175. http_status=$(curl -sI -o /dev/null -w "%{http_code}" "https://github.com/Kilo-Org/kilocode/releases/tag/v${requested_version}")
  176. if [ "$http_status" = "404" ]; then
  177. echo -e "${RED}Error: Release v${requested_version} not found${NC}"
  178. echo -e "${MUTED}Available releases: https://github.com/Kilo-Org/kilocode/releases${NC}"
  179. exit 1
  180. fi
  181. fi
  182. fi
  183. print_message() {
  184. local level=$1
  185. local message=$2
  186. local color=""
  187. case $level in
  188. info) color="${NC}" ;;
  189. warning) color="${NC}" ;;
  190. error) color="${RED}" ;;
  191. esac
  192. echo -e "${color}${message}${NC}"
  193. }
  194. # kilocode_change start
  195. check_version() {
  196. if command -v kilo >/dev/null 2>&1; then
  197. kilo_path=$(which kilo)
  198. ## Check the installed version
  199. installed_version=$(kilo --version 2>/dev/null || echo "")
  200. if [[ "$installed_version" != "$specific_version" ]]; then
  201. print_message info "${MUTED}Installed version: ${NC}$installed_version."
  202. else
  203. print_message info "${MUTED}Version ${NC}$specific_version${MUTED} already installed"
  204. exit 0
  205. fi
  206. fi
  207. }
  208. # kilocode_change end
  209. unbuffered_sed() {
  210. if echo | sed -u -e "" >/dev/null 2>&1; then
  211. sed -nu "$@"
  212. elif echo | sed -l -e "" >/dev/null 2>&1; then
  213. sed -nl "$@"
  214. else
  215. local pad="$(printf "\n%512s" "")"
  216. sed -ne "s/$/\\${pad}/" "$@"
  217. fi
  218. }
  219. print_progress() {
  220. local bytes="$1"
  221. local length="$2"
  222. [ "$length" -gt 0 ] || return 0
  223. local width=50
  224. local percent=$(( bytes * 100 / length ))
  225. [ "$percent" -gt 100 ] && percent=100
  226. local on=$(( percent * width / 100 ))
  227. local off=$(( width - on ))
  228. local filled=$(printf "%*s" "$on" "")
  229. filled=${filled// /■}
  230. local empty=$(printf "%*s" "$off" "")
  231. empty=${empty// /・}
  232. printf "\r${ORANGE}%s%s %3d%%${NC}" "$filled" "$empty" "$percent" >&4
  233. }
  234. download_with_progress() {
  235. local url="$1"
  236. local output="$2"
  237. if [ -t 2 ]; then
  238. exec 4>&2
  239. else
  240. exec 4>/dev/null
  241. fi
  242. local tmp_dir=${TMPDIR:-/tmp}
  243. local basename="${tmp_dir}/kilo_install_$$" # kilocode_change
  244. local tracefile="${basename}.trace"
  245. rm -f "$tracefile"
  246. mkfifo "$tracefile"
  247. # Hide cursor
  248. printf "\033[?25l" >&4
  249. trap "trap - RETURN; rm -f \"$tracefile\"; printf '\033[?25h' >&4; exec 4>&-" RETURN
  250. (
  251. curl --trace-ascii "$tracefile" -fsL -o "$output" "$url" # kilocode_change
  252. ) &
  253. local curl_pid=$!
  254. unbuffered_sed \
  255. -e 'y/ACDEGHLNORTV/acdeghlnortv/' \
  256. -e '/^0000: content-length:/p' \
  257. -e '/^<= recv data/p' \
  258. "$tracefile" | \
  259. {
  260. local length=0
  261. local bytes=0
  262. while IFS=" " read -r -a line; do
  263. [ "${#line[@]}" -lt 2 ] && continue
  264. local tag="${line[0]} ${line[1]}"
  265. if [ "$tag" = "0000: content-length:" ]; then
  266. length="${line[2]}"
  267. length=$(echo "$length" | tr -d '\r')
  268. bytes=0
  269. elif [ "$tag" = "<= recv" ]; then
  270. local size="${line[3]}"
  271. bytes=$(( bytes + size ))
  272. if [ "$length" -gt 0 ]; then
  273. print_progress "$bytes" "$length"
  274. fi
  275. fi
  276. done
  277. }
  278. wait $curl_pid
  279. local ret=$?
  280. echo "" >&4
  281. return $ret
  282. }
  283. download_and_install() {
  284. # kilocode_change start
  285. print_message info "\n${MUTED}Installing ${NC}Kilo Code ${MUTED}version: ${NC}$specific_version"
  286. local tmp_dir="${TMPDIR:-/tmp}/kilo_install_$$"
  287. # kilocode_change end
  288. mkdir -p "$tmp_dir"
  289. if [[ "$os" == "windows" ]] || ! [ -t 2 ] || ! download_with_progress "$url" "$tmp_dir/$filename"; then
  290. # Fallback to standard curl on Windows, non-TTY environments, or if custom progress fails
  291. curl -f# -L -o "$tmp_dir/$filename" "$url" # kilocode_change
  292. fi
  293. if [ "$os" = "linux" ]; then
  294. tar -xzf "$tmp_dir/$filename" -C "$tmp_dir"
  295. else
  296. unzip -q "$tmp_dir/$filename" -d "$tmp_dir"
  297. fi
  298. # kilocode_change start
  299. mv "$tmp_dir/kilo" "$INSTALL_DIR"
  300. chmod 755 "${INSTALL_DIR}/kilo"
  301. # kilocode_change end
  302. rm -rf "$tmp_dir"
  303. }
  304. install_from_binary() {
  305. # kilocode_change start
  306. print_message info "\n${MUTED}Installing ${NC}Kilo Code ${MUTED}from: ${NC}$binary_path"
  307. cp "$binary_path" "${INSTALL_DIR}/kilo"
  308. chmod 755 "${INSTALL_DIR}/kilo"
  309. # kilocode_change end
  310. }
  311. if [ -n "$binary_path" ]; then
  312. install_from_binary
  313. else
  314. check_version
  315. download_and_install
  316. fi
  317. add_to_path() {
  318. local config_file=$1
  319. local command=$2
  320. if grep -Fxq "$command" "$config_file"; then
  321. print_message info "Command already exists in $config_file, skipping write."
  322. elif [[ -w $config_file ]]; then
  323. echo -e "\n# kilo" >> "$config_file" # kilocode_change
  324. echo "$command" >> "$config_file"
  325. print_message info "${MUTED}Successfully added ${NC}kilo ${MUTED}to \$PATH in ${NC}$config_file" # kilocode_change
  326. else
  327. print_message warning "Manually add the directory to $config_file (or similar):"
  328. print_message info " $command"
  329. fi
  330. }
  331. XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-$HOME/.config}
  332. current_shell=$(basename "$SHELL")
  333. case $current_shell in
  334. fish)
  335. config_files="$HOME/.config/fish/config.fish"
  336. ;;
  337. zsh)
  338. config_files="${ZDOTDIR:-$HOME}/.zshrc ${ZDOTDIR:-$HOME}/.zshenv $XDG_CONFIG_HOME/zsh/.zshrc $XDG_CONFIG_HOME/zsh/.zshenv"
  339. ;;
  340. bash)
  341. config_files="$HOME/.bashrc $HOME/.bash_profile $HOME/.profile $XDG_CONFIG_HOME/bash/.bashrc $XDG_CONFIG_HOME/bash/.bash_profile"
  342. ;;
  343. ash)
  344. config_files="$HOME/.ashrc $HOME/.profile /etc/profile"
  345. ;;
  346. sh)
  347. config_files="$HOME/.ashrc $HOME/.profile /etc/profile"
  348. ;;
  349. *)
  350. # Default case if none of the above matches
  351. config_files="$HOME/.bashrc $HOME/.bash_profile $XDG_CONFIG_HOME/bash/.bashrc $XDG_CONFIG_HOME/bash/.bash_profile"
  352. ;;
  353. esac
  354. if [[ "$no_modify_path" != "true" ]]; then
  355. config_file=""
  356. for file in $config_files; do
  357. if [[ -f $file ]]; then
  358. config_file=$file
  359. break
  360. fi
  361. done
  362. if [[ -z $config_file ]]; then
  363. print_message warning "No config file found for $current_shell. You may need to manually add to PATH:"
  364. print_message info " export PATH=$INSTALL_DIR:\$PATH"
  365. elif [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
  366. case $current_shell in
  367. fish)
  368. add_to_path "$config_file" "fish_add_path $INSTALL_DIR"
  369. ;;
  370. zsh)
  371. add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
  372. ;;
  373. bash)
  374. add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
  375. ;;
  376. ash)
  377. add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
  378. ;;
  379. sh)
  380. add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
  381. ;;
  382. *)
  383. export PATH=$INSTALL_DIR:$PATH
  384. print_message warning "Manually add the directory to $config_file (or similar):"
  385. print_message info " export PATH=$INSTALL_DIR:\$PATH"
  386. ;;
  387. esac
  388. fi
  389. fi
  390. if [ -n "${GITHUB_ACTIONS-}" ] && [ "${GITHUB_ACTIONS}" == "true" ]; then
  391. echo "$INSTALL_DIR" >> $GITHUB_PATH
  392. print_message info "Added $INSTALL_DIR to \$GITHUB_PATH"
  393. fi
  394. # kilocode_change start
  395. echo -e ""
  396. echo -e "██ ▄█▀ ██ ██ ▄████▄ ▄█████ ██ ██ "
  397. echo -e "████ ██ ██ ██ ██ ██ ██ ██ "
  398. echo -e "██ ▀█▄ ██ ██████ ▀████▀ ▀█████ ██████ ██ "
  399. echo -e ""
  400. echo -e ""
  401. echo -e "${MUTED}Kilo includes free models so you can start immediately:${NC}"
  402. echo -e ""
  403. echo -e "cd <project> ${MUTED}# Open directory${NC}"
  404. echo -e "kilo ${MUTED}# Run Kilo${NC}"
  405. echo -e ""
  406. echo -e "${MUTED}For more information visit ${NC}https://kilo.ai/docs"
  407. echo -e ""
  408. echo -e ""
  409. # kilocode_change end