#!/usr/bin/env bash # CutOS Agent installer # Run: curl -fsSL https://git.cutos.ai/claw-daemon/cutos-agent/raw/main/install.sh | bash set -euo pipefail RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' info() { echo -e "${GREEN}[cutos-agent]${NC} $*"; } warn() { echo -e "${YELLOW}[cutos-agent]${NC} $*"; } error() { echo -e "${RED}[cutos-agent]${NC} $*" >&2; exit 1; } require_command() { if ! command -v "$1" >/dev/null 2>&1; then error "$1 not found. Please install $1 and run this installer again." fi } run_root() { if [ "${EUID:-$(id -u)}" -eq 0 ]; then "$@" return fi require_command sudo sudo "$@" } detect_user() { if [ "${EUID:-$(id -u)}" -eq 0 ]; then if [ -n "${SUDO_USER:-}" ] && [ "${SUDO_USER}" != "root" ]; then printf '%s' "$SUDO_USER" else printf 'root' fi else id -un fi } detect_home() { local user="$1" local home="" if command -v getent >/dev/null 2>&1; then home="$(getent passwd "$user" | cut -d: -f6 || true)" fi if [ -z "$home" ]; then home="${HOME:-}" fi if [ -z "$home" ]; then error "Cannot determine home directory for user $user" fi printf '%s' "$home" } npm_global_install() { info "Installing $* ..." if npm install -g "$@"; then return fi if [ "${EUID:-$(id -u)}" -eq 0 ]; then error "npm install failed: $*" fi warn "Retrying global npm install with sudo ..." run_root env PATH="$PATH" npm install -g "$@" } run_as_target_user() { if [ "$TARGET_USER" = "root" ]; then HOME="$TARGET_HOME" "$@" return fi if [ "${EUID:-$(id -u)}" -eq 0 ]; then runuser -u "$TARGET_USER" -- env HOME="$TARGET_HOME" PATH="$PATH" "$@" else HOME="$TARGET_HOME" "$@" fi } install_pi_package() { local package="$1" info "Installing Pi extension: $package" run_as_target_user "$PI_BIN" install "$package" } resolve_bin() { local name="$1" local found="" found="$(command -v "$name" || true)" if [ -n "$found" ]; then printf '%s' "$found" return fi local prefix="" prefix="$(npm prefix -g 2>/dev/null || true)" if [ -n "$prefix" ] && [ -x "$prefix/bin/$name" ]; then printf '%s' "$prefix/bin/$name" return fi if [ -x "/usr/local/bin/$name" ]; then printf '%s' "/usr/local/bin/$name" return fi if [ -x "/usr/bin/$name" ]; then printf '%s' "/usr/bin/$name" return fi error "Cannot find $name after installation" } write_pi_web_service() { local pi_web_bin="$1" local service_file="/etc/systemd/system/pi-web.service" info "Writing pi-web systemd service ..." run_root tee "$service_file" >/dev/null </dev/null fi if run_root grep -q '^[[:space:]]*PI_MODELS_CONFIG=' "$env_file"; then run_root sed -i "s|^[[:space:]]*PI_MODELS_CONFIG=.*|PI_MODELS_CONFIG=$models_config|" "$env_file" elif run_root grep -q '^[[:space:]]*# PI_MODELS_CONFIG=' "$env_file"; then run_root sed -i "s|^[[:space:]]*# PI_MODELS_CONFIG=.*|PI_MODELS_CONFIG=$models_config|" "$env_file" else printf 'PI_MODELS_CONFIG=%s\n' "$models_config" | run_root tee -a "$env_file" >/dev/null fi else run_root tee "$env_file" >/dev/null </dev/null 2>&1 && run_root systemctl cat clawd.service >/dev/null 2>&1; then info "Restarting clawd to apply AGENT_TYPE ..." if ! run_root systemctl restart clawd.service; then warn "clawd restart failed. Check logs with: journalctl -u clawd -n 50 --no-pager" fi fi } require_command node require_command npm NODE_VER="$(node -e "process.stdout.write(process.versions.node)")" NODE_MAJOR="$(printf '%s' "$NODE_VER" | cut -d. -f1)" if [ "$NODE_MAJOR" -lt 18 ]; then error "Node.js $NODE_VER is too old. Please install Node.js >= 18." fi info "Node.js $NODE_VER OK" TARGET_USER="$(detect_user)" TARGET_HOME="$(detect_home "$TARGET_USER")" TARGET_GROUP="$(id -gn "$TARGET_USER" 2>/dev/null || printf '%s' "$TARGET_USER")" PI_HOME="$TARGET_HOME/.pi" info "Installing for user: $TARGET_USER" info "Pi working directory: $PI_HOME" configure_clawd_agent_type run_root mkdir -p "$PI_HOME" run_root mkdir -p "$PI_HOME/agent" if [ "$TARGET_USER" != "root" ]; then run_root chown "$TARGET_USER:$TARGET_GROUP" "$PI_HOME" run_root chown "$TARGET_USER:$TARGET_GROUP" "$PI_HOME/agent" fi npm_global_install @earendil-works/pi-coding-agent@latest --allow-scripts=@google/genai,protobufjs,sharp npm_global_install @agegr/pi-web PI_BIN="$(resolve_bin pi)" PI_WEB_BIN="$(resolve_bin pi-web)" info "pi CLI: $PI_BIN" info "pi-web: $PI_WEB_BIN" install_pi_package npm:pi-wechat-assistant install_pi_package npm:@jay-zod/speakturbo install_pi_package npm:pi-codex-image-gen install_pi_package npm:@mammothb/pi-office install_pi_package npm:@zosmaai/pi-llm-wiki install_pi_package npm:pi-git-commands-extension install_pi_package npm:@xjuai/pi-feishu-lark install_pi_package npm:pi-ocr install_pi_package npm:pi-schedule-prompt write_pi_web_service "$PI_WEB_BIN" sleep 2 if systemctl is-active --quiet pi-web.service; then info "pi-web is running" else warn "pi-web failed to start. Check logs with: journalctl -u pi-web -n 50 --no-pager" fi cat <