From 85b7906b30a819ec9017d1389e0b4b8d48b268ac Mon Sep 17 00:00:00 2001 From: Ujang Date: Thu, 11 Dec 2025 09:31:07 +0700 Subject: [PATCH] feat: Add Linux bash scripts for installation and management Added Linux-ready equivalents of Windows PowerShell scripts: - install-cliproxyapi.sh: Complete installer (prebuilt/source build) - start-cliproxyapi.sh: Server management (start/stop/restart/status) - update-cliproxyapi.sh: Update to latest version - uninstall-cliproxyapi.sh: Clean uninstallation - cliproxyapi-oauth.sh: OAuth login helper for all providers All scripts support the same functionality as their Windows counterparts with proper Linux conventions (bash, systemd-friendly background mode). --- scripts/linux/cliproxyapi-oauth.sh | 98 +++++++++++++ scripts/linux/install-cliproxyapi.sh | 187 +++++++++++++++++++++++++ scripts/linux/start-cliproxyapi.sh | 100 +++++++++++++ scripts/linux/uninstall-cliproxyapi.sh | 79 +++++++++++ scripts/linux/update-cliproxyapi.sh | 93 ++++++++++++ 5 files changed, 557 insertions(+) create mode 100755 scripts/linux/cliproxyapi-oauth.sh create mode 100755 scripts/linux/install-cliproxyapi.sh create mode 100755 scripts/linux/start-cliproxyapi.sh create mode 100755 scripts/linux/uninstall-cliproxyapi.sh create mode 100755 scripts/linux/update-cliproxyapi.sh diff --git a/scripts/linux/cliproxyapi-oauth.sh b/scripts/linux/cliproxyapi-oauth.sh new file mode 100755 index 0000000..a2dc79c --- /dev/null +++ b/scripts/linux/cliproxyapi-oauth.sh @@ -0,0 +1,98 @@ +#!/bin/bash +# +# CLIProxyAPI-Plus OAuth Login Helper for Linux +# Usage: +# ./cliproxyapi-oauth.sh # Interactive menu +# ./cliproxyapi-oauth.sh --all # Login to all +# ./cliproxyapi-oauth.sh --gemini # Gemini only +# ./cliproxyapi-oauth.sh --copilot # GitHub Copilot +# + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' +CYAN='\033[0;36m'; MAGENTA='\033[0;35m'; NC='\033[0m' + +CONFIG="$HOME/.cli-proxy-api/config.yaml" +BINARY="$HOME/bin/cliproxyapi-plus" + +[ ! -f "$BINARY" ] && echo -e "${RED}[-] Binary not found. Run install-cliproxyapi.sh${NC}" && exit 1 + +declare -A FLAGS=( + ["gemini"]="--login" + ["antigravity"]="--antigravity-login" + ["copilot"]="--github-copilot-login" + ["codex"]="--codex-login" + ["claude"]="--claude-login" + ["qwen"]="--qwen-login" + ["iflow"]="--iflow-login" + ["kiro"]="--kiro-aws-login" +) + +declare -A NAMES=( + ["gemini"]="Gemini CLI" + ["antigravity"]="Antigravity" + ["copilot"]="GitHub Copilot" + ["codex"]="Codex" + ["claude"]="Claude" + ["qwen"]="Qwen" + ["iflow"]="iFlow" + ["kiro"]="Kiro (AWS)" +) + +ORDER=("gemini" "antigravity" "copilot" "codex" "claude" "qwen" "iflow" "kiro") + +run_login() { + local key="$1" + echo -e "\n${CYAN}[*] Logging in to ${NAMES[$key]}...${NC}" + "$BINARY" --config "$CONFIG" "${FLAGS[$key]}" + [ $? -eq 0 ] && echo -e "${GREEN}[+] ${NAMES[$key]} login completed!${NC}" || echo -e "${YELLOW}[!] ${NAMES[$key]} may have issues${NC}" +} + +LOGIN_ALL=false +SELECTED=() + +while [[ $# -gt 0 ]]; do + case $1 in + --all|-a) LOGIN_ALL=true; shift ;; + --gemini) SELECTED+=("gemini"); shift ;; + --antigravity) SELECTED+=("antigravity"); shift ;; + --copilot) SELECTED+=("copilot"); shift ;; + --codex) SELECTED+=("codex"); shift ;; + --claude) SELECTED+=("claude"); shift ;; + --qwen) SELECTED+=("qwen"); shift ;; + --iflow) SELECTED+=("iflow"); shift ;; + --kiro) SELECTED+=("kiro"); shift ;; + *) shift ;; + esac +done + +if [ "$LOGIN_ALL" = true ] || [ ${#SELECTED[@]} -gt 0 ]; then + echo -e "${MAGENTA}=== CLIProxyAPI-Plus OAuth Login ===${NC}" + if [ "$LOGIN_ALL" = true ]; then + for key in "${ORDER[@]}"; do run_login "$key"; done + else + for key in "${SELECTED[@]}"; do run_login "$key"; done + fi +else + echo -e "${MAGENTA} +========================================== + CLIProxyAPI-Plus OAuth Login Menu +========================================== +${NC}" + echo -e "${YELLOW}Available providers:${NC}" + for i in "${!ORDER[@]}"; do echo " $((i+1)). ${NAMES[${ORDER[$i]}]}"; done + echo " A. Login to ALL" + echo " Q. Quit" + + while true; do + read -p "Select [1-8, A, Q]: " choice + [[ "$choice" =~ ^[Qq]$ ]] && echo "Bye!" && break + [[ "$choice" =~ ^[Aa]$ ]] && { for k in "${ORDER[@]}"; do run_login "$k"; done; break; } + [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le 8 ] && run_login "${ORDER[$((choice-1))]}" + done +fi + +echo -e "${GREEN} +========================================== + Auth files saved in: $HOME/.cli-proxy-api +========================================== +${NC}" diff --git a/scripts/linux/install-cliproxyapi.sh b/scripts/linux/install-cliproxyapi.sh new file mode 100755 index 0000000..da694f9 --- /dev/null +++ b/scripts/linux/install-cliproxyapi.sh @@ -0,0 +1,187 @@ +#!/bin/bash +# +# CLIProxyAPI-Plus Installation Script for Linux +# Usage: +# ./install-cliproxyapi.sh # Auto install +# ./install-cliproxyapi.sh --prebuilt # Force prebuilt binary +# ./install-cliproxyapi.sh --source # Force build from source +# ./install-cliproxyapi.sh --skip-oauth # Skip OAuth info +# ./install-cliproxyapi.sh --force # Overwrite existing config +# + +set -e + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' +CYAN='\033[0;36m'; MAGENTA='\033[0;35m'; NC='\033[0m' + +REPO_URL="https://github.com/router-for-me/CLIProxyAPIPlus.git" +RELEASE_API="https://api.github.com/repos/router-for-me/CLIProxyAPIPlus/releases/latest" +CLONE_DIR="$HOME/CLIProxyAPIPlus" +BIN_DIR="$HOME/bin" +CONFIG_DIR="$HOME/.cli-proxy-api" +FACTORY_DIR="$HOME/.factory" +BINARY_NAME="cliproxyapi-plus" + +USE_PREBUILT=false; FORCE_SOURCE=false; SKIP_OAUTH=false; FORCE=false + +while [[ $# -gt 0 ]]; do + case $1 in + --prebuilt) USE_PREBUILT=true; shift ;; + --source) FORCE_SOURCE=true; shift ;; + --skip-oauth) SKIP_OAUTH=true; shift ;; + --force) FORCE=true; shift ;; + *) shift ;; + esac +done + +write_step() { echo -e "\n${CYAN}[*] $1${NC}"; } +write_success() { echo -e "${GREEN}[+] $1${NC}"; } +write_warning() { echo -e "${YELLOW}[!] $1${NC}"; } +write_error() { echo -e "${RED}[-] $1${NC}"; } + +echo -e "${MAGENTA} +============================================== + CLIProxyAPI-Plus Installer for Linux +============================================== +${NC}" + +write_step "Checking prerequisites..." + +if [ "$USE_PREBUILT" = false ] && [ "$FORCE_SOURCE" = false ]; then + if command -v go &> /dev/null; then + write_success "Go found: $(go version)" + else + write_warning "Go not installed. Using prebuilt binary." + USE_PREBUILT=true + fi +fi + +command -v git &> /dev/null || { write_error "Git not installed"; exit 1; } +write_success "Git found" + +command -v curl &> /dev/null && DOWNLOAD_CMD="curl" || DOWNLOAD_CMD="wget" +command -v jq &> /dev/null && HAS_JQ=true || HAS_JQ=false + +write_step "Creating directories..." +mkdir -p "$BIN_DIR" "$CONFIG_DIR" "$FACTORY_DIR" +write_success "Directories ready" + +ARCH=$(uname -m) +case $ARCH in + x86_64) ARCH_SUFFIX="linux_amd64" ;; + aarch64|arm64) ARCH_SUFFIX="linux_arm64" ;; + *) ARCH_SUFFIX="linux_amd64" ;; +esac + +if [ "$USE_PREBUILT" = true ]; then + write_step "Downloading pre-built binary..." + TEMP_DIR=$(mktemp -d) + + if [ "$HAS_JQ" = true ]; then + RELEASE_INFO=$(curl -s -H "User-Agent: Bash" "$RELEASE_API") + DOWNLOAD_URL=$(echo "$RELEASE_INFO" | jq -r ".assets[] | select(.name | contains(\"$ARCH_SUFFIX\")) | .browser_download_url" | head -n1) + else + RELEASE_INFO=$(curl -s -H "User-Agent: Bash" "$RELEASE_API") + DOWNLOAD_URL=$(echo "$RELEASE_INFO" | grep -o "https://[^\"]*${ARCH_SUFFIX}[^\"]*" | head -n1) + fi + + if [ -z "$DOWNLOAD_URL" ] || [ "$DOWNLOAD_URL" = "null" ]; then + write_warning "No prebuilt found, building from source..." + FORCE_SOURCE=true + else + echo " Downloading..." + curl -sL -o "$TEMP_DIR/archive.tar.gz" "$DOWNLOAD_URL" + cd "$TEMP_DIR" + tar -xzf archive.tar.gz 2>/dev/null || unzip -q archive.tar.gz 2>/dev/null || true + BINARY_FILE=$(find . -type f -name "cliproxyapi*" ! -name "*.gz" ! -name "*.zip" | head -n1) + if [ -n "$BINARY_FILE" ]; then + chmod +x "$BINARY_FILE" + mv "$BINARY_FILE" "$BIN_DIR/$BINARY_NAME" + write_success "Binary installed: $BIN_DIR/$BINARY_NAME" + fi + rm -rf "$TEMP_DIR" + fi +fi + +if [ "$FORCE_SOURCE" = true ] || [ ! -f "$BIN_DIR/$BINARY_NAME" ]; then + write_step "Building from source..." + [ -d "$CLONE_DIR" ] && [ "$FORCE" = true ] && rm -rf "$CLONE_DIR" + [ ! -d "$CLONE_DIR" ] && git clone --depth 1 "$REPO_URL" "$CLONE_DIR" + cd "$CLONE_DIR" + go build -o "$BIN_DIR/$BINARY_NAME" ./cmd/server + write_success "Binary built: $BIN_DIR/$BINARY_NAME" +fi + +write_step "Creating config.yaml..." +CONFIG_PATH="$CONFIG_DIR/config.yaml" +if [ -f "$CONFIG_PATH" ] && [ "$FORCE" = false ]; then + write_warning "config.yaml exists, skipping" +else + cat > "$CONFIG_PATH" << EOF +port: 8317 +auth-dir: "$CONFIG_DIR" +api-keys: + - "sk-dummy" +quota-exceeded: + switch-project: true + switch-preview-model: true +incognito-browser: true +request-retry: 3 +remote-management: + allow-remote: false + secret-key: "" + disable-control-panel: false +EOF + write_success "config.yaml created" +fi + +write_step "Creating .factory/config.json..." +cat > "$FACTORY_DIR/config.json" << 'EOF' +{ + "custom_models": [ + { "model_display_name": "Claude Opus 4.5 Thinking [Antigravity]", "model": "gemini-claude-opus-4-5-thinking", "base_url": "http://localhost:8317/v1", "api_key": "sk-dummy", "provider": "openai" }, + { "model_display_name": "Claude Sonnet 4.5 [Antigravity]", "model": "gemini-claude-sonnet-4-5", "base_url": "http://localhost:8317/v1", "api_key": "sk-dummy", "provider": "openai" }, + { "model_display_name": "Gemini 2.5 Pro [Gemini]", "model": "gemini-2.5-pro", "base_url": "http://localhost:8317/v1", "api_key": "sk-dummy", "provider": "openai" }, + { "model_display_name": "Claude Opus 4.5 [Copilot]", "model": "claude-opus-4.5", "base_url": "http://localhost:8317/v1", "api_key": "sk-dummy", "provider": "openai" }, + { "model_display_name": "GPT-5.1 Codex Max [Codex]", "model": "gpt-5.1-codex-max", "base_url": "http://localhost:8317/v1", "api_key": "sk-dummy", "provider": "openai" }, + { "model_display_name": "Qwen3 Coder Plus [Qwen]", "model": "qwen3-coder-plus", "base_url": "http://localhost:8317/v1", "api_key": "sk-dummy", "provider": "openai" }, + { "model_display_name": "Claude Opus 4.5 [Kiro]", "model": "kiro-claude-opus-4.5", "base_url": "http://localhost:8317/v1", "api_key": "sk-dummy", "provider": "openai" } + ] +} +EOF +write_success "config.json created with custom models" + +write_step "Verifying installation..." +[ -f "$BIN_DIR/$BINARY_NAME" ] && write_success "Binary verified" || { write_error "Binary not found"; exit 1; } + +write_step "Configuring PATH..." +SHELL_RC="$HOME/.bashrc"; [ -f "$HOME/.zshrc" ] && SHELL_RC="$HOME/.zshrc" +if ! grep -q "$BIN_DIR" "$SHELL_RC" 2>/dev/null; then + echo -e "\n# CLIProxyAPI-Plus\nexport PATH=\"\$PATH:$BIN_DIR\"" >> "$SHELL_RC" + write_success "Added to PATH in $SHELL_RC" +fi + +[ "$SKIP_OAUTH" = false ] && echo -e "${YELLOW} +OAuth Login Commands: + $BINARY_NAME --config $CONFIG_DIR/config.yaml --login # Gemini + $BINARY_NAME --config $CONFIG_DIR/config.yaml --antigravity-login # Antigravity + $BINARY_NAME --config $CONFIG_DIR/config.yaml --github-copilot-login + $BINARY_NAME --config $CONFIG_DIR/config.yaml --codex-login + $BINARY_NAME --config $CONFIG_DIR/config.yaml --claude-login + $BINARY_NAME --config $CONFIG_DIR/config.yaml --qwen-login + $BINARY_NAME --config $CONFIG_DIR/config.yaml --kiro-aws-login +${NC}" + +echo -e "${GREEN} +============================================== + Installation Complete! +============================================== +Binary: $BIN_DIR/$BINARY_NAME +Config: $CONFIG_DIR/config.yaml +Droid: $FACTORY_DIR/config.json + +Quick Start: + source $SHELL_RC + start-cliproxyapi.sh --background +============================================== +${NC}" diff --git a/scripts/linux/start-cliproxyapi.sh b/scripts/linux/start-cliproxyapi.sh new file mode 100755 index 0000000..99eb6e8 --- /dev/null +++ b/scripts/linux/start-cliproxyapi.sh @@ -0,0 +1,100 @@ +#!/bin/bash +# +# CLIProxyAPI-Plus Server Manager for Linux +# Usage: +# ./start-cliproxyapi.sh # Start foreground +# ./start-cliproxyapi.sh --background # Start background +# ./start-cliproxyapi.sh --status # Check status +# ./start-cliproxyapi.sh --stop # Stop server +# ./start-cliproxyapi.sh --restart # Restart +# ./start-cliproxyapi.sh --logs # View logs +# + +set -e + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' +CYAN='\033[0;36m'; MAGENTA='\033[0;35m'; NC='\033[0m' + +BINARY="$HOME/bin/cliproxyapi-plus" +CONFIG="$HOME/.cli-proxy-api/config.yaml" +LOG_DIR="$HOME/.cli-proxy-api/logs" +PID_FILE="$HOME/.cli-proxy-api/server.pid" +PORT=8317 + +BACKGROUND=false; STATUS=false; STOP=false; LOGS=false; RESTART=false + +while [[ $# -gt 0 ]]; do + case $1 in + --background|-b) BACKGROUND=true; shift ;; + --status|-s) STATUS=true; shift ;; + --stop) STOP=true; shift ;; + --restart|-r) RESTART=true; shift ;; + --logs|-l) LOGS=true; shift ;; + *) shift ;; + esac +done + +get_server_pid() { + [ -f "$PID_FILE" ] && PID=$(cat "$PID_FILE") && ps -p "$PID" &>/dev/null && echo "$PID" && return + pgrep -f "cliproxyapi-plus" 2>/dev/null | head -n1 +} + +show_status() { + echo -e "\n${MAGENTA}=== CLIProxyAPI-Plus Status ===${NC}" + PID=$(get_server_pid) + if [ -n "$PID" ]; then + echo -e "${GREEN}[+] Server RUNNING (PID: $PID)${NC}" + [ -f "/proc/$PID/status" ] && MEM=$(grep VmRSS /proc/$PID/status | awk '{print $2}') && echo " Memory: $((MEM/1024)) MB" + else + echo -e "${YELLOW}[!] Server NOT running${NC}" + fi + ss -tuln 2>/dev/null | grep -q ":$PORT " && echo -e "${GREEN}Port $PORT in use${NC}" || echo -e "${YELLOW}Port $PORT free${NC}" + curl -s -o /dev/null -w "%{http_code}" --connect-timeout 2 "http://localhost:$PORT/v1/models" | grep -q "200\|401" && echo -e "${GREEN}[+] API responding${NC}" + echo "" +} + +stop_server() { + PID=$(get_server_pid) + if [ -n "$PID" ]; then + echo -e "${CYAN}[*] Stopping server (PID: $PID)...${NC}" + kill "$PID" 2>/dev/null; sleep 0.5 + ps -p "$PID" &>/dev/null && kill -9 "$PID" 2>/dev/null + rm -f "$PID_FILE" + echo -e "${GREEN}[+] Server stopped${NC}" + else + echo -e "${YELLOW}[!] Server not running${NC}" + fi +} + +show_logs() { + mkdir -p "$LOG_DIR" + LATEST=$(ls -t "$LOG_DIR"/*.log 2>/dev/null | head -n1) + [ -n "$LATEST" ] && tail -f "$LATEST" || echo -e "${YELLOW}[!] No logs found${NC}" +} + +start_server() { + PID=$(get_server_pid) + [ -n "$PID" ] && echo -e "${YELLOW}[!] Already running (PID: $PID)${NC}" && show_status && return + [ ! -f "$BINARY" ] && echo -e "${RED}[-] Binary not found. Run install-cliproxyapi.sh${NC}" && exit 1 + [ ! -f "$CONFIG" ] && echo -e "${RED}[-] Config not found. Run install-cliproxyapi.sh${NC}" && exit 1 + + mkdir -p "$LOG_DIR" + + if [ "$BACKGROUND" = true ]; then + echo -e "${CYAN}[*] Starting in background...${NC}" + LOG_FILE="$LOG_DIR/server-$(date +%Y%m%d).log" + nohup "$BINARY" --config "$CONFIG" >> "$LOG_FILE" 2>&1 & + echo $! > "$PID_FILE" + sleep 2 + ps -p $(cat "$PID_FILE") &>/dev/null && echo -e "${GREEN}[+] Started (PID: $(cat $PID_FILE))${NC}\nEndpoint: http://localhost:$PORT/v1" || echo -e "${RED}[-] Failed to start${NC}" + else + echo -e "${MAGENTA}=== CLIProxyAPI-Plus ===${NC}\nConfig: $CONFIG\nEndpoint: http://localhost:$PORT/v1\n${YELLOW}Ctrl+C to stop${NC}\n" + exec "$BINARY" --config "$CONFIG" + fi +} + +[ "$STATUS" = true ] && show_status && exit 0 +[ "$STOP" = true ] && stop_server && exit 0 +[ "$LOGS" = true ] && show_logs && exit 0 +[ "$RESTART" = true ] && stop_server && sleep 1 +start_server diff --git a/scripts/linux/uninstall-cliproxyapi.sh b/scripts/linux/uninstall-cliproxyapi.sh new file mode 100755 index 0000000..b38a847 --- /dev/null +++ b/scripts/linux/uninstall-cliproxyapi.sh @@ -0,0 +1,79 @@ +#!/bin/bash +# +# CLIProxyAPI-Plus Uninstaller for Linux +# Usage: +# ./uninstall-cliproxyapi.sh # Keep auth files +# ./uninstall-cliproxyapi.sh --all # Remove everything +# ./uninstall-cliproxyapi.sh --force # No confirmation +# + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' +CYAN='\033[0;36m'; NC='\033[0m' + +BIN_DIR="$HOME/bin" +CONFIG_DIR="$HOME/.cli-proxy-api" +CLONE_DIR="$HOME/CLIProxyAPIPlus" +FACTORY_CONFIG="$HOME/.factory/config.json" + +REMOVE_ALL=false; FORCE=false + +while [[ $# -gt 0 ]]; do + case $1 in + --all|-a) REMOVE_ALL=true; shift ;; + --force|-f) FORCE=true; shift ;; + *) shift ;; + esac +done + +echo -e "${RED} +========================================== + CLIProxyAPI-Plus Uninstaller +========================================== +${NC}" + +echo -e "${CYAN}[*] Scanning installation...${NC}" + +# Stop server first +PID=$(pgrep -f "cliproxyapi-plus" 2>/dev/null | head -n1) +[ -n "$PID" ] && echo -e "${CYAN}[*] Stopping server...${NC}" && kill "$PID" 2>/dev/null + +echo -e "\n${RED}[!] Will be REMOVED:${NC}" +[ -f "$BIN_DIR/cliproxyapi-plus" ] && echo " - Binary: $BIN_DIR/cliproxyapi-plus" +[ -d "$CLONE_DIR" ] && echo " - Source: $CLONE_DIR" +[ -f "$CONFIG_DIR/config.yaml" ] && echo " - Config: $CONFIG_DIR/config.yaml" +[ -d "$CONFIG_DIR/logs" ] && echo " - Logs: $CONFIG_DIR/logs" + +if [ "$REMOVE_ALL" = false ]; then + echo -e "\n${GREEN}[*] Will be KEPT:${NC}" + ls "$CONFIG_DIR"/*.json 2>/dev/null | while read f; do echo " - Auth: $f"; done + echo -e " ${YELLOW}Use --all to remove everything${NC}" +fi + +if [ "$FORCE" = false ]; then + read -p "Are you sure? [y/N] " confirm + [[ ! "$confirm" =~ ^[Yy]$ ]] && echo -e "${YELLOW}Cancelled${NC}" && exit 0 +fi + +echo -e "\n${CYAN}[*] Removing...${NC}" + +rm -f "$BIN_DIR/cliproxyapi-plus" "$BIN_DIR/cliproxyapi-plus.old" && echo -e "${GREEN}[+] Binary removed${NC}" +rm -rf "$CLONE_DIR" && echo -e "${GREEN}[+] Source removed${NC}" +rm -f "$CONFIG_DIR/config.yaml" "$CONFIG_DIR/server.pid" && echo -e "${GREEN}[+] Config removed${NC}" +rm -rf "$CONFIG_DIR/logs" && echo -e "${GREEN}[+] Logs removed${NC}" + +if [ "$REMOVE_ALL" = true ]; then + rm -f "$CONFIG_DIR"/*.json && echo -e "${GREEN}[+] Auth files removed${NC}" + rmdir "$CONFIG_DIR" 2>/dev/null +fi + +# Clear custom_models from factory config +if [ -f "$FACTORY_CONFIG" ] && command -v jq &>/dev/null; then + jq '.custom_models = []' "$FACTORY_CONFIG" > "$FACTORY_CONFIG.tmp" && mv "$FACTORY_CONFIG.tmp" "$FACTORY_CONFIG" + echo -e "${GREEN}[+] Cleared custom_models from Droid config${NC}" +fi + +echo -e "${GREEN} +========================================== + Uninstall Complete! +========================================== +${NC}" diff --git a/scripts/linux/update-cliproxyapi.sh b/scripts/linux/update-cliproxyapi.sh new file mode 100755 index 0000000..b3d5818 --- /dev/null +++ b/scripts/linux/update-cliproxyapi.sh @@ -0,0 +1,93 @@ +#!/bin/bash +# +# CLIProxyAPI-Plus Update Script for Linux +# Usage: +# ./update-cliproxyapi.sh # Auto update +# ./update-cliproxyapi.sh --prebuilt # Force prebuilt +# ./update-cliproxyapi.sh --force # Force rebuild +# + +set -e + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' +CYAN='\033[0;36m'; MAGENTA='\033[0;35m'; NC='\033[0m' + +REPO_URL="https://github.com/router-for-me/CLIProxyAPIPlus.git" +RELEASE_API="https://api.github.com/repos/router-for-me/CLIProxyAPIPlus/releases/latest" +CLONE_DIR="$HOME/CLIProxyAPIPlus" +BIN_DIR="$HOME/bin" +CONFIG_DIR="$HOME/.cli-proxy-api" +BINARY_NAME="cliproxyapi-plus" +BINARY_PATH="$BIN_DIR/$BINARY_NAME" + +USE_PREBUILT=false; FORCE=false + +while [[ $# -gt 0 ]]; do + case $1 in + --prebuilt) USE_PREBUILT=true; shift ;; + --force) FORCE=true; shift ;; + *) shift ;; + esac +done + +echo -e "${MAGENTA} +============================================== + CLIProxyAPI-Plus Updater +============================================== +${NC}" + +echo -e "${CYAN}[*] Checking current installation...${NC}" +[ ! -f "$BINARY_PATH" ] && echo -e "${RED}[-] Binary not found. Run install first.${NC}" && exit 1 +echo " Current: $(stat -c %y "$BINARY_PATH" 2>/dev/null | cut -d' ' -f1)" + +ARCH=$(uname -m) +case $ARCH in + x86_64) ARCH_SUFFIX="linux_amd64" ;; + aarch64|arm64) ARCH_SUFFIX="linux_arm64" ;; + *) ARCH_SUFFIX="linux_amd64" ;; +esac + +if [ "$USE_PREBUILT" = false ] && [ -d "$CLONE_DIR" ]; then + echo -e "${CYAN}[*] Updating from source...${NC}" + cd "$CLONE_DIR" + git fetch origin main 2>/dev/null || git fetch origin master + + LOCAL=$(git rev-parse HEAD) + REMOTE=$(git rev-parse origin/main 2>/dev/null || git rev-parse origin/master) + + [ "$LOCAL" = "$REMOTE" ] && [ "$FORCE" = false ] && echo -e "${GREEN}[+] Already up to date!${NC}" && exit 0 + + git pull origin main --rebase 2>/dev/null || git reset --hard origin/main 2>/dev/null || git reset --hard origin/master + go build -o "$BINARY_PATH" ./cmd/server + echo -e "${GREEN}[+] Binary rebuilt${NC}" +else + echo -e "${CYAN}[*] Downloading prebuilt...${NC}" + TEMP_DIR=$(mktemp -d) + + if command -v jq &>/dev/null; then + DOWNLOAD_URL=$(curl -s "$RELEASE_API" | jq -r ".assets[] | select(.name | contains(\"$ARCH_SUFFIX\")) | .browser_download_url" | head -n1) + else + DOWNLOAD_URL=$(curl -s "$RELEASE_API" | grep -o "https://[^\"]*${ARCH_SUFFIX}[^\"]*" | head -n1) + fi + + [ -z "$DOWNLOAD_URL" ] && echo -e "${RED}[-] No binary found${NC}" && exit 1 + + curl -sL -o "$TEMP_DIR/archive.tar.gz" "$DOWNLOAD_URL" + cd "$TEMP_DIR" + tar -xzf archive.tar.gz 2>/dev/null || unzip -q archive.tar.gz 2>/dev/null || true + + BINARY_FILE=$(find . -type f -name "cliproxyapi*" ! -name "*.gz" ! -name "*.zip" | head -n1) + [ -n "$BINARY_FILE" ] && cp "$BINARY_PATH" "$BINARY_PATH.old" 2>/dev/null; chmod +x "$BINARY_FILE"; mv "$BINARY_FILE" "$BINARY_PATH" + rm -rf "$TEMP_DIR" + echo -e "${GREEN}[+] Binary updated${NC}" +fi + +echo -e "${GREEN} +============================================== + Update Complete! +============================================== +Binary: $BINARY_PATH +Config: $CONFIG_DIR/config.yaml (preserved) +Auth: $CONFIG_DIR/*.json (preserved) +============================================== +${NC}"