diff --git a/README.md b/README.md index 63c4ade..c1095e9 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ gui-cliproxyapi ``` **Available Scripts:** + - `start-cliproxyapi` - Start/stop/restart server - `cliproxyapi-oauth` - Login to OAuth providers - `gui-cliproxyapi` - Open GUI Control Center @@ -116,6 +117,87 @@ gui-cliproxyapi --- +## Quick Start (Linux/macOS) + +### Prerequisites + +- **Git** - Usually pre-installed, or install with `sudo apt install git` (Ubuntu) / `brew install git` (macOS) +- **Go 1.21+** (optional, for building from source) - [Download](https://go.dev/dl/) +- **curl** or **wget** - Usually pre-installed + +### Installation + +```bash +# Clone this repo +git clone https://github.com/julianromli/CLIProxyAPIPlus-Easy-Installation.git +cd CLIProxyAPIPlus-Easy-Installation + +# Make script executable and run installer +chmod +x scripts/install-cliproxyapi.sh +./scripts/install-cliproxyapi.sh + +# Or use pre-built binary (no Go required) +./scripts/install-cliproxyapi.sh --use-prebuilt +``` + +### After Installation + +Scripts are installed to `~/.local/bin/` and added to PATH automatically. + +```bash +# Reload shell config (or open new terminal) +source ~/.bashrc # or ~/.zshrc if using zsh + +# Start server in background +start-cliproxyapi --background + +# Login to providers +cliproxyapi-oauth --all + +# Open GUI Control Center (full control via browser) +gui-cliproxyapi +``` + +**Available Scripts:** + +- `start-cliproxyapi` - Start/stop/restart server +- `cliproxyapi-oauth` - Login to OAuth providers +- `gui-cliproxyapi` - Open GUI Control Center +- `update-cliproxyapi` - Update to latest version +- `uninstall-cliproxyapi` - Remove everything + +### Testing the Installation + +After installation, test if everything works: + +```bash +# 1. Check server status +start-cliproxyapi --status + +# 2. Test API endpoint +curl -H "Authorization: Bearer sk-dummy" http://localhost:8317/v1/models + +# 3. Test chat completion +curl http://localhost:8317/v1/chat/completions \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer sk-dummy" \ + -d '{ + "model": "gemini-2.5-pro", + "messages": [{"role": "user", "content": "Hello!"}] + }' + +# 4. View server logs +start-cliproxyapi --logs +``` + +Expected results: + +- `/v1/models` returns list of available models +- Chat completion returns AI response +- Logs show successful API calls + +--- + ## Usage with Different CLI Tools ### Factory Droid @@ -143,8 +225,9 @@ gui-cliproxyapi Set environment variables before running: +**Windows (PowerShell):** + ```powershell -# PowerShell $env:ANTHROPIC_BASE_URL = "http://localhost:8317/v1" $env:ANTHROPIC_API_KEY = "sk-dummy" claude @@ -160,6 +243,24 @@ $env:ANTHROPIC_BASE_URL = "http://localhost:8317/v1" $env:ANTHROPIC_API_KEY = "sk-dummy" ``` +**Linux/macOS (Bash/Zsh):** + +```bash +export ANTHROPIC_BASE_URL="http://localhost:8317/v1" +export ANTHROPIC_API_KEY="sk-dummy" +claude + +# Or in one line +ANTHROPIC_BASE_URL="http://localhost:8317/v1" ANTHROPIC_API_KEY="sk-dummy" claude +``` + +For persistent config, add to `~/.bashrc` or `~/.zshrc`: + +```bash +export ANTHROPIC_BASE_URL="http://localhost:8317/v1" +export ANTHROPIC_API_KEY="sk-dummy" +``` + ### OpenCode Create or edit `~/.opencode/config.json`: @@ -201,7 +302,7 @@ Edit `~/.continue/config.json`: }, { "title": "CLIProxy - Claude", - "provider": "openai", + "provider": "openai", "model": "claude-opus-4.5", "apiKey": "sk-dummy", "apiBase": "http://localhost:8317/v1" @@ -244,6 +345,7 @@ curl http://localhost:8317/v1/chat/completions \ ## Available Models ### Antigravity Provider + | Model ID | Description | |----------|-------------| | `gemini-claude-opus-4-5-thinking` | Claude Opus 4.5 with extended thinking | @@ -253,6 +355,7 @@ curl http://localhost:8317/v1/chat/completions \ | `gpt-oss-120b-medium` | GPT OSS 120B | ### GitHub Copilot Provider + | Model ID | Description | |----------|-------------| | `claude-opus-4.5` | Claude Opus 4.5 | @@ -260,28 +363,33 @@ curl http://localhost:8317/v1/chat/completions \ | `grok-code-fast-1` | Grok Code Fast | ### Gemini CLI Provider + | Model ID | Description | |----------|-------------| | `gemini-2.5-pro` | Gemini 2.5 Pro | | `gemini-3-pro-preview` | Gemini 3 Pro Preview | ### Codex Provider + | Model ID | Description | |----------|-------------| | `gpt-5.1-codex-max` | GPT-5.1 Codex Max | ### Qwen Provider + | Model ID | Description | |----------|-------------| | `qwen3-coder-plus` | Qwen3 Coder Plus | ### iFlow Provider + | Model ID | Description | |----------|-------------| | `glm-4.6` | GLM 4.6 | | `minimax-m2` | Minimax M2 | ### Kiro (AWS) Provider + | Model ID | Description | |----------|-------------| | `kiro-claude-opus-4.5` | Claude Opus 4.5 via Kiro | @@ -396,6 +504,7 @@ gui-cliproxyapi.ps1 -NoBrowser ``` **Features:** + - Real-time server status monitoring - Start/Stop/Restart buttons (actually work!) - OAuth login buttons for all providers @@ -474,6 +583,7 @@ MIT License - See [LICENSE](LICENSE) file. ## Contributing PRs welcome! Feel free to: + - Add support for more CLI tools - Improve documentation - Report bugs diff --git a/README_ID.md b/README_ID.md index 2de79b1..a278502 100644 --- a/README_ID.md +++ b/README_ID.md @@ -108,12 +108,94 @@ gui-cliproxyapi ``` **Script yang Tersedia:** + +- `start-cliproxyapi` - Start/stop/restart server +- `cliproxyapi-oauth` - Login ke OAuth provider +- `gui-cliproxyapi` - Buka GUI Control Center +- `update-cliproxyapi` - Update ke versi terbaru +- `uninstall-cliproxyapi` - Hapus semuanya + +--- + +## Quick Start (Linux/macOS) + +### Prasyarat + +- **Git** - Biasanya sudah terinstall, atau install dengan `sudo apt install git` (Ubuntu) / `brew install git` (macOS) +- **Go 1.21+** (opsional, untuk build dari source) - [Download](https://go.dev/dl/) +- **curl** atau **wget** - Biasanya sudah terinstall + +### Instalasi + +```bash +# Clone repo ini +git clone https://github.com/julianromli/CLIProxyAPIPlus-Easy-Installation.git +cd CLIProxyAPIPlus-Easy-Installation + +# Buat script executable dan jalankan installer +chmod +x scripts/install-cliproxyapi.sh +./scripts/install-cliproxyapi.sh + +# Atau pake binary pre-built (gak perlu Go) +./scripts/install-cliproxyapi.sh --use-prebuilt +``` + +### Setelah Instalasi + +Script diinstall ke `~/.local/bin/` dan otomatis ditambahkan ke PATH. + +```bash +# Reload shell config (atau buka terminal baru) +source ~/.bashrc # atau ~/.zshrc kalau pake zsh + +# Start server di background +start-cliproxyapi --background + +# Login ke provider +cliproxyapi-oauth --all + +# Buka GUI Control Center (kontrol penuh via browser) +gui-cliproxyapi +``` + +**Script yang Tersedia:** + - `start-cliproxyapi` - Start/stop/restart server - `cliproxyapi-oauth` - Login ke OAuth provider - `gui-cliproxyapi` - Buka GUI Control Center - `update-cliproxyapi` - Update ke versi terbaru - `uninstall-cliproxyapi` - Hapus semuanya +### Testing Instalasi + +Setelah instalasi, cek apakah semuanya berjalan: + +```bash +# 1. Cek status server +start-cliproxyapi --status + +# 2. Test endpoint API +curl -H "Authorization: Bearer sk-dummy" http://localhost:8317/v1/models + +# 3. Test chat completion +curl http://localhost:8317/v1/chat/completions \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer sk-dummy" \ + -d '{ + "model": "gemini-2.5-pro", + "messages": [{"role": "user", "content": "Halo!"}] + }' + +# 4. Lihat log server +start-cliproxyapi --logs +``` + +Hasil yang diharapkan: + +- `/v1/models` mengembalikan daftar model +- Chat completion mengembalikan respon AI +- Log menunjukkan API call berhasil + --- ## Penggunaan dengan Berbagai CLI Tool @@ -143,8 +225,9 @@ gui-cliproxyapi Set environment variable sebelum running: +**Windows (PowerShell):** + ```powershell -# PowerShell $env:ANTHROPIC_BASE_URL = "http://localhost:8317/v1" $env:ANTHROPIC_API_KEY = "sk-dummy" claude @@ -160,6 +243,24 @@ $env:ANTHROPIC_BASE_URL = "http://localhost:8317/v1" $env:ANTHROPIC_API_KEY = "sk-dummy" ``` +**Linux/macOS (Bash/Zsh):** + +```bash +export ANTHROPIC_BASE_URL="http://localhost:8317/v1" +export ANTHROPIC_API_KEY="sk-dummy" +claude + +# Atau dalam satu baris +ANTHROPIC_BASE_URL="http://localhost:8317/v1" ANTHROPIC_API_KEY="sk-dummy" claude +``` + +Untuk config permanen, tambahkan ke `~/.bashrc` atau `~/.zshrc`: + +```bash +export ANTHROPIC_BASE_URL="http://localhost:8317/v1" +export ANTHROPIC_API_KEY="sk-dummy" +``` + ### OpenCode Buat atau edit `~/.opencode/config.json`: @@ -201,7 +302,7 @@ Edit `~/.continue/config.json`: }, { "title": "CLIProxy - Claude", - "provider": "openai", + "provider": "openai", "model": "claude-opus-4.5", "apiKey": "sk-dummy", "apiBase": "http://localhost:8317/v1" @@ -244,6 +345,7 @@ curl http://localhost:8317/v1/chat/completions \ ## Model yang Tersedia ### Provider Antigravity + | Model ID | Deskripsi | |----------|-----------| | `gemini-claude-opus-4-5-thinking` | Claude Opus 4.5 dengan extended thinking | @@ -253,6 +355,7 @@ curl http://localhost:8317/v1/chat/completions \ | `gpt-oss-120b-medium` | GPT OSS 120B | ### Provider GitHub Copilot + | Model ID | Deskripsi | |----------|-----------| | `claude-opus-4.5` | Claude Opus 4.5 | @@ -260,28 +363,33 @@ curl http://localhost:8317/v1/chat/completions \ | `grok-code-fast-1` | Grok Code Fast | ### Provider Gemini CLI + | Model ID | Deskripsi | |----------|-----------| | `gemini-2.5-pro` | Gemini 2.5 Pro | | `gemini-3-pro-preview` | Gemini 3 Pro Preview | ### Provider Codex + | Model ID | Deskripsi | |----------|-----------| | `gpt-5.1-codex-max` | GPT-5.1 Codex Max | ### Provider Qwen + | Model ID | Deskripsi | |----------|-----------| | `qwen3-coder-plus` | Qwen3 Coder Plus | ### Provider iFlow + | Model ID | Deskripsi | |----------|-----------| | `glm-4.6` | GLM 4.6 | | `minimax-m2` | Minimax M2 | ### Provider Kiro (AWS) + | Model ID | Deskripsi | |----------|-----------| | `kiro-claude-opus-4.5` | Claude Opus 4.5 via Kiro | @@ -396,6 +504,7 @@ gui-cliproxyapi.ps1 -NoBrowser ``` **Fitur:** + - Monitoring status server real-time - Tombol Start/Stop/Restart (beneran jalan!) - Tombol OAuth login untuk semua provider @@ -474,6 +583,7 @@ MIT License - Lihat file [LICENSE](LICENSE). ## Kontribusi PR welcome! Silakan: + - Tambah dukungan untuk CLI tool lain - Perbaiki dokumentasi - Laporkan bug diff --git a/scripts/AGENTS.md b/scripts/AGENTS.md index 6d88d5a..cd08d77 100644 --- a/scripts/AGENTS.md +++ b/scripts/AGENTS.md @@ -1,26 +1,43 @@ # AGENTS.md - Scripts Directory -> Detailed guidance for PowerShell scripts in this repository. +> Detailed guidance for installation and management scripts in this repository. ## Package Identity - **Purpose**: Automation scripts for CLIProxyAPIPlus installation/management -- **Language**: PowerShell 5.1+ (Windows-native) +- **Platform Support**: Windows (PowerShell 5.1+), Linux/macOS (Bash) - **No dependencies**: Scripts are self-contained ## Script Inventory +### PowerShell Scripts (Windows) + | Script | Purpose | Key Params | |--------|---------|------------| | `install-cliproxyapi.ps1` | Full installation | `-UsePrebuilt`, `-Force`, `-SkipOAuth` | +| `start-cliproxyapi.ps1` | Server manager | `-Background`, `-Stop`, `-Restart`, `-Status`, `-Logs` | | `update-cliproxyapi.ps1` | Update to latest | `-UsePrebuilt`, `-Force` | | `cliproxyapi-oauth.ps1` | OAuth login helper | `-All`, `-Gemini`, `-Copilot`, etc. | -| `uninstall-cliproxyapi.ps1` | Clean removal | `-All`, `-KeepAuth`, `-Force` | +| `gui-cliproxyapi.ps1` | Web GUI launcher | `-Port`, `-NoBrowser` | +| `uninstall-cliproxyapi.ps1` | Clean removal | `-All`, `-Force` | + +### Bash Scripts (Linux/macOS) + +| Script | Purpose | Key Params | +|--------|---------|------------| +| `install-cliproxyapi.sh` | Full installation | `--use-prebuilt`, `--force`, `--skip-oauth` | +| `start-cliproxyapi.sh` | Server manager | `--background`, `--stop`, `--restart`, `--status`, `--logs` | +| `update-cliproxyapi.sh` | Update to latest | `--use-prebuilt`, `--force` | +| `cliproxyapi-oauth.sh` | OAuth login helper | `--all`, `--gemini`, `--copilot`, etc. | +| `gui-cliproxyapi.sh` | Web GUI launcher | `--port`, `--no-browser` | +| `uninstall-cliproxyapi.sh` | Clean removal | `--all`, `--force` | ## Patterns & Conventions ### Script Header Template + Every script MUST have this structure: + ```powershell <# .SYNOPSIS @@ -41,20 +58,26 @@ param( $ErrorActionPreference = "Stop" ``` + ✅ DO: Copy from `install-cliproxyapi.ps1` header ### Output Functions + Use consistent colored output: + ```powershell function Write-Step { param($msg) Write-Host "`n[*] $msg" -ForegroundColor Cyan } function Write-Success { param($msg) Write-Host "[+] $msg" -ForegroundColor Green } function Write-Warning { param($msg) Write-Host "[!] $msg" -ForegroundColor Yellow } function Write-Error { param($msg) Write-Host "[-] $msg" -ForegroundColor Red } ``` + ✅ DO: See `install-cliproxyapi.ps1` lines 30-33 ### Path Variables + Always use expandable paths: + ```powershell # ✅ DO: Use environment variables $CONFIG_DIR = "$env:USERPROFILE\.cli-proxy-api" @@ -65,6 +88,7 @@ $CONFIG_DIR = "C:\Users\faiz\.cli-proxy-api" ``` ### Error Handling + ```powershell # ✅ DO: Use try-catch with specific error messages try { @@ -79,12 +103,13 @@ Some-Operation 2>$null ``` ### External Commands + ```powershell # ✅ DO: Check exit codes & git clone $REPO_URL $CLONE_DIR -if ($LASTEXITCODE -ne 0) { +if ($LASTEXITCODE -ne 0) { Write-Error "Git clone failed" - exit 1 + exit 1 } # ❌ DON'T: Assume success @@ -109,6 +134,7 @@ if ($LASTEXITCODE -ne 0) { 6. Add to README_ID.md (Indonesian version) ### Checklist for New Script + ```powershell # Verify these patterns: Select-String -Path "scripts\NEW_SCRIPT.ps1" -Pattern "\.SYNOPSIS" # Has help @@ -117,8 +143,118 @@ Select-String -Path "scripts\NEW_SCRIPT.ps1" -Pattern "ErrorActionPreference" # Select-String -Path "scripts\NEW_SCRIPT.ps1" -Pattern "env:USERPROFILE" # Uses env vars ``` +## Bash Script Patterns (Linux/macOS) + +### Script Header Template + +```bash +#!/usr/bin/env bash +# +# Script Description +# +# Detailed explanation of what this script does +# +# Author: ... +# Repo: https://github.com/... + +set -e # Exit on error + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --flag) FLAG=true; shift ;; + --param) PARAM="$2"; shift 2 ;; + *) echo "Unknown option: $1"; exit 1 ;; + esac +done +``` + +✅ DO: Copy from `install-cliproxyapi.sh` header + +### Output Functions + +Use consistent colored output: + +```bash +# Colors +GREEN='\033[0;32m' +CYAN='\033[0;36m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +echo_step() { echo -e "\n${CYAN}[*] $1${NC}"; } +echo_success() { echo -e "${GREEN}[+] $1${NC}"; } +echo_warning() { echo -e "${YELLOW}[!] $1${NC}"; } +echo_error() { echo -e "${RED}[-] $1${NC}"; } +``` + +### Path Variables + +Use HOME and XDG conventions: + +```bash +BIN_DIR="$HOME/.local/bin" +CONFIG_DIR="$HOME/.cli-proxy-api" +FACTORY_DIR="$HOME/.factory" +``` + +### Error Handling + +```bash +set -e # Exit on any error + +# Check command availability +if ! command -v git &> /dev/null; then + echo_error "Git is not installed" + exit 1 +fi + +# Check file existence +if [ ! -f "$FILE" ]; then + echo_error "File not found: $FILE" + exit 1 +fi +``` + +### Cross-Platform Compatibility + +```bash +# Detect OS and architecture +OS="$(uname -s | tr '[:upper:]' '[:lower:]')" +ARCH="$(uname -m)" + +case "$ARCH" in + x86_64) ARCH="amd64" ;; + aarch64|arm64) ARCH="arm64" ;; + *) echo_error "Unsupported: $ARCH"; exit 1 ;; +esac + +# Detect shell config file +if [ -n "$BASH_VERSION" ]; then + SHELL_RC="$HOME/.bashrc" +elif [ -n "$ZSH_VERSION" ]; then + SHELL_RC="$HOME/.zshrc" +else + SHELL_RC="$HOME/.profile" +fi +``` + +### Checklist for New Bash Script + +```bash +# Verify before commit: +grep -q "#!/usr/bin/env bash" script.sh # Has shebang +grep -q "set -e" script.sh # Has error handling +grep -q "echo_" script.sh # Uses output functions +chmod +x script.sh # Is executable +shellcheck script.sh || true # Run linter (if available) +``` + ## JIT Index Hints +### PowerShell + ```powershell # Find all param definitions Select-String -Path "*.ps1" -Pattern "\[switch\]|\[string\]" @@ -133,18 +269,45 @@ Select-String -Path "*.ps1" -Pattern "& \w+" Select-String -Path "*.ps1" -Pattern "exit \d" ``` +### Bash + +```bash +# Find all function definitions +grep -n "^[a-z_]*() {" scripts/*.sh + +# Find all echo_* calls (for UI consistency) +grep -n "echo_\(step\|success\|warning\|error\)" scripts/*.sh + +# Find all exit points +grep -n "exit [0-9]" scripts/*.sh + +# Find command checks +grep -n "command -v" scripts/*.sh +``` + ## Common Gotchas -1. **PowerShell string escaping**: Use single quotes for literals, double for variables +### PowerShell + +1. **String escaping**: Use single quotes for literals, double for variables 2. **Path separators**: Use `\` on Windows, but `/` works in most contexts 3. **Invoke-WebRequest**: May fail without `-UseBasicParsing` on older systems 4. **JSON depth**: `ConvertTo-Json` defaults to depth 2, use `-Depth 10` for nested +### Bash + +1. **Quoting variables**: Always quote: `"$VAR"` not `$VAR` (handles spaces) +2. **Command substitution**: Use `$(command)` not backticks +3. **Test expressions**: Use `[[ ]]` not `[ ]` (more robust) +4. **Temporary files**: Use `mktemp` for secure temp file creation +5. **Cross-platform paths**: Use `$HOME` not `~` in scripts +6. **Exit on error**: Always use `set -e` at script start + ## Pre-PR Checks ```powershell # Syntax check all scripts -Get-ChildItem *.ps1 | ForEach-Object { +Get-ChildItem *.ps1 | ForEach-Object { $null = [System.Management.Automation.PSParser]::Tokenize((Get-Content $_.FullName -Raw), [ref]$null) Write-Host "✓ $($_.Name)" -ForegroundColor Green } diff --git a/scripts/cliproxyapi-api-server.py b/scripts/cliproxyapi-api-server.py new file mode 100644 index 0000000..47e5090 --- /dev/null +++ b/scripts/cliproxyapi-api-server.py @@ -0,0 +1,309 @@ +#!/usr/bin/env python3 +""" +Simple HTTP API server for CLIProxyAPI-Plus management +Provides REST API endpoints for the GUI Control Center +""" + +import http.server +import socketserver +import json +import subprocess +import os +from urllib.parse import urlparse, parse_qs +from pathlib import Path + +PORT = 8318 +HOME = Path.home() +BIN_DIR = HOME / ".local" / "bin" +CONFIG_DIR = HOME / ".cli-proxy-api" + +class APIHandler(http.server.SimpleHTTPRequestHandler): + """Handler for API requests""" + + def do_GET(self): + """Handle GET requests""" + parsed = urlparse(self.path) + + if parsed.path == '/api/status': + self.handle_status() + elif parsed.path == '/api/config': + self.handle_get_config() + elif parsed.path == '/api/auth-status': + self.handle_auth_status() + elif parsed.path == '/api/models': + self.handle_models() + elif parsed.path == '/api/stats': + self.handle_stats() + elif parsed.path == '/api/update/check': + self.handle_update_check() + else: + # Serve static files (GUI) + super().do_GET() + + def do_POST(self): + """Handle POST requests""" + parsed = urlparse(self.path) + + if parsed.path == '/api/start': + self.handle_start() + elif parsed.path == '/api/stop': + self.handle_stop() + elif parsed.path == '/api/restart': + self.handle_restart() + elif parsed.path.startswith('/api/oauth/'): + provider = parsed.path.split('/')[-1] + self.handle_oauth(provider) + elif parsed.path == '/api/update/apply': + self.handle_update_apply() + else: + self.send_error(404) + + def handle_status(self): + """Get server status""" + try: + result = subprocess.run( + [str(BIN_DIR / "start-cliproxyapi"), "--status"], + capture_output=True, + text=True, + timeout=5 + ) + + # Parse output + is_running = "Server is running" in result.stdout + pid = None + if is_running: + for line in result.stdout.split('\n'): + if 'PID:' in line: + pid = line.split('PID:')[-1].strip().rstrip(')') + + response = { + "running": is_running, + "pid": pid, + "endpoint": "http://localhost:8317/v1" if is_running else None + } + + self.send_json(response) + except Exception as e: + self.send_json({"running": False, "error": str(e)}) + + def handle_start(self): + """Start the server""" + try: + result = subprocess.run( + [str(BIN_DIR / "start-cliproxyapi"), "--background"], + capture_output=True, + text=True, + timeout=10 + ) + + success = result.returncode == 0 + self.send_json({ + "success": success, + "message": result.stdout + result.stderr + }) + except Exception as e: + self.send_json({"success": False, "message": str(e)}) + + def handle_stop(self): + """Stop the server""" + try: + result = subprocess.run( + [str(BIN_DIR / "start-cliproxyapi"), "--stop"], + capture_output=True, + text=True, + timeout=10 + ) + + success = result.returncode == 0 + self.send_json({ + "success": success, + "message": result.stdout + result.stderr + }) + except Exception as e: + self.send_json({"success": False, "message": str(e)}) + + def handle_restart(self): + """Restart the server""" + try: + result = subprocess.run( + [str(BIN_DIR / "start-cliproxyapi"), "--restart"], + capture_output=True, + text=True, + timeout=15 + ) + + success = result.returncode == 0 + self.send_json({ + "success": success, + "message": result.stdout + result.stderr + }) + except Exception as e: + self.send_json({"success": False, "message": str(e)}) + + def handle_oauth(self, provider): + """Trigger OAuth login""" + provider_map = { + "gemini": "--gemini", + "antigravity": "--antigravity", + "copilot": "--copilot", + "codex": "--codex", + "claude": "--claude", + "qwen": "--qwen", + "iflow": "--iflow", + "kiro": "--kiro" + } + + flag = provider_map.get(provider.lower()) + if not flag: + self.send_json({"success": False, "message": "Unknown provider"}) + return + + try: + # Run in background, don't wait + subprocess.Popen( + [str(BIN_DIR / "cliproxyapi-oauth"), flag], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL + ) + + self.send_json({ + "success": True, + "message": f"OAuth login started for {provider}" + }) + except Exception as e: + self.send_json({"success": False, "message": str(e)}) + + def handle_get_config(self): + """Get current config""" + config_file = CONFIG_DIR / "config.yaml" + try: + if config_file.exists(): + with open(config_file, 'r') as f: + self.send_json({"config": f.read()}) + else: + self.send_json({"config": ""}) + except Exception as e: + self.send_json({"config": "", "error": str(e)}) + + def handle_auth_status(self): + """Get auth status for providers""" + try: + auth_files = list(CONFIG_DIR.glob("*.json")) + providers = { + "gemini": False, + "antigravity": False, + "copilot": False, + "codex": False, + "claude": False, + "qwen": False, + "iflow": False, + "kiro": False + } + + for f in auth_files: + name = f.stem.lower() + if "gemini" in name or "@gmail.com" in name: + providers["gemini"] = True + if "antigravity" in name: + providers["antigravity"] = True + if "copilot" in name or "github" in name: + providers["copilot"] = True + if "codex" in name: + providers["codex"] = True + if "claude" in name: + providers["claude"] = True + if "qwen" in name: + providers["qwen"] = True + if "iflow" in name: + providers["iflow"] = True + if "kiro" in name: + providers["kiro"] = True + + self.send_json(providers) + except Exception as e: + self.send_json({p: False for p in ["gemini", "antigravity", "copilot", "codex", "claude", "qwen", "iflow", "kiro"]}) + + def handle_models(self): + """Get available models""" + try: + result = subprocess.run( + ["curl", "-s", "-H", "Authorization: Bearer sk-dummy", + "http://localhost:8317/v1/models"], + capture_output=True, + text=True, + timeout=5 + ) + + if result.returncode == 0: + data = json.loads(result.stdout) + models = [m["id"] for m in data.get("data", [])] + self.send_json({"models": models}) + else: + self.send_json({"models": []}) + except Exception as e: + self.send_json({"models": [], "error": str(e)}) + + def handle_stats(self): + """Get request statistics (stub for now)""" + self.send_json({ + "totalRequests": 0, + "successRate": 0, + "avgLatency": 0, + "errors": 0 + }) + + def handle_update_check(self): + """Check for updates (stub)""" + self.send_json({ + "available": False, + "current": "6.5.64", + "latest": "6.5.64" + }) + + def handle_update_apply(self): + """Apply update""" + try: + # Run update in background + subprocess.Popen( + [str(BIN_DIR / "update-cliproxyapi"), "--use-prebuilt"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL + ) + + self.send_json({ + "success": True, + "message": "Update started in background" + }) + except Exception as e: + self.send_json({"success": False, "message": str(e)}) + + def send_json(self, data): + """Send JSON response""" + self.send_response(200) + self.send_header('Content-Type', 'application/json') + self.send_header('Access-Control-Allow-Origin', '*') + self.end_headers() + self.wfile.write(json.dumps(data).encode()) + + def log_message(self, format, *args): + """Override to reduce noise""" + pass + +def main(): + """Start the API server""" + # Change to GUI directory + gui_dir = HOME / ".local" / "share" / "cliproxyapi" / "gui" + if gui_dir.exists(): + os.chdir(gui_dir) + + with socketserver.TCPServer(("", PORT), APIHandler) as httpd: + print(f"API Server running on http://localhost:{PORT}") + print(f"GUI available at http://localhost:{PORT}") + print("Press Ctrl+C to stop") + try: + httpd.serve_forever() + except KeyboardInterrupt: + print("\nShutting down...") + +if __name__ == "__main__": + main() diff --git a/scripts/cliproxyapi-oauth.sh b/scripts/cliproxyapi-oauth.sh new file mode 100755 index 0000000..e9468d9 --- /dev/null +++ b/scripts/cliproxyapi-oauth.sh @@ -0,0 +1,252 @@ +#!/usr/bin/env bash +# +# CLIProxyAPI-Plus OAuth Login Helper +# +# Interactive OAuth login for all supported providers + +set -e + +BINARY_PATH="$HOME/.local/bin/cliproxyapi-plus" +CONFIG_PATH="$HOME/.cli-proxy-api/config.yaml" + +# Colors +GREEN='\033[0;32m' +CYAN='\033[0;36m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +MAGENTA='\033[0;35m' +NC='\033[0m' + +echo_info() { echo -e "${CYAN}[*] $1${NC}"; } +echo_success() { echo -e "${GREEN}[+] $1${NC}"; } +echo_warning() { echo -e "${YELLOW}[!] $1${NC}"; } +echo_error() { echo -e "${RED}[-] $1${NC}"; } + +# Check prerequisites +if [ ! -f "$BINARY_PATH" ]; then + echo_error "Binary not found: $BINARY_PATH" + echo_error "Run install-cliproxyapi first" + exit 1 +fi + +if [ ! -f "$CONFIG_PATH" ]; then + echo_error "Config not found: $CONFIG_PATH" + echo_error "Run install-cliproxyapi first" + exit 1 +fi + +# Login functions +login_gemini() { + echo_info "Logging into Gemini CLI..." + "$BINARY_PATH" --config "$CONFIG_PATH" --login + echo_success "Gemini login complete" +} + +login_antigravity() { + echo_info "Logging into Antigravity..." + "$BINARY_PATH" --config "$CONFIG_PATH" --antigravity-login + echo_success "Antigravity login complete" +} + +login_copilot() { + echo_info "Logging into GitHub Copilot..." + "$BINARY_PATH" --config "$CONFIG_PATH" --github-copilot-login + echo_success "GitHub Copilot login complete" +} + +login_codex() { + echo_info "Logging into Codex..." + "$BINARY_PATH" --config "$CONFIG_PATH" --codex-login + echo_success "Codex login complete" +} + +login_claude() { + echo_info "Logging into Claude..." + "$BINARY_PATH" --config "$CONFIG_PATH" --claude-login + echo_success "Claude login complete" +} + +login_qwen() { + echo_info "Logging into Qwen..." + "$BINARY_PATH" --config "$CONFIG_PATH" --qwen-login + echo_success "Qwen login complete" +} + +login_iflow() { + echo_info "Logging into iFlow..." + "$BINARY_PATH" --config "$CONFIG_PATH" --iflow-login + echo_success "iFlow login complete" +} + +login_kiro() { + echo_info "Logging into Kiro (AWS)..." + "$BINARY_PATH" --config "$CONFIG_PATH" --kiro-aws-login + echo_success "Kiro login complete" +} + +# Login to all providers +login_all() { + echo -e "${MAGENTA}" + cat << "EOF" +============================================== + Login to All Providers +============================================== +EOF + echo -e "${NC}" + + echo_warning "This will open multiple browser windows for OAuth login." + echo_warning "Press Enter to continue or Ctrl+C to cancel..." + read + + PROVIDERS=("Gemini" "Antigravity" "GitHub Copilot" "Codex" "Claude" "Qwen" "iFlow" "Kiro") + FUNCS=(login_gemini login_antigravity login_copilot login_codex login_claude login_qwen login_iflow login_kiro) + + for i in "${!PROVIDERS[@]}"; do + echo "" + echo_info "[$((i+1))/${#PROVIDERS[@]}] ${PROVIDERS[$i]}" + ${FUNCS[$i]} || echo_warning "Failed to login to ${PROVIDERS[$i]}" + sleep 1 + done + + echo "" + echo_success "All OAuth logins complete!" +} + +# Interactive menu +show_menu() { + echo -e "${MAGENTA}" + cat << "EOF" +============================================== + CLIProxyAPI-Plus OAuth Login +============================================== +EOF + echo -e "${NC}" + + echo "Select provider to login:" + echo " 1) Gemini CLI" + echo " 2) Antigravity" + echo " 3) GitHub Copilot" + echo " 4) Codex" + echo " 5) Claude" + echo " 6) Qwen" + echo " 7) iFlow" + echo " 8) Kiro (AWS)" + echo " 9) All providers" + echo " 0) Exit" + echo "" + read -p "Enter choice [0-9]: " choice + + case $choice in + 1) login_gemini ;; + 2) login_antigravity ;; + 3) login_copilot ;; + 4) login_codex ;; + 5) login_claude ;; + 6) login_qwen ;; + 7) login_iflow ;; + 8) login_kiro ;; + 9) login_all ;; + 0) exit 0 ;; + *) echo_error "Invalid choice"; exit 1 ;; + esac +} + +# Show help +show_help() { + cat << EOF +CLIProxyAPI-Plus OAuth Login Helper + +Usage: + cliproxyapi-oauth [OPTIONS] + +Options: + --all Login to all providers + --gemini Login to Gemini CLI + --antigravity Login to Antigravity + --copilot Login to GitHub Copilot + --codex Login to Codex + --claude Login to Claude + --qwen Login to Qwen + --iflow Login to iFlow + --kiro Login to Kiro (AWS) + --help, -h Show this help + +Examples: + cliproxyapi-oauth # Interactive menu + cliproxyapi-oauth --all # Login to all + cliproxyapi-oauth --gemini --copilot # Login to specific providers +EOF +} + +# Parse arguments +if [ $# -eq 0 ]; then + show_menu + exit 0 +fi + +LOGIN_ANY=false + +while [[ $# -gt 0 ]]; do + case $1 in + --all) + login_all + LOGIN_ANY=true + shift + ;; + --gemini) + login_gemini + LOGIN_ANY=true + shift + ;; + --antigravity) + login_antigravity + LOGIN_ANY=true + shift + ;; + --copilot) + login_copilot + LOGIN_ANY=true + shift + ;; + --codex) + login_codex + LOGIN_ANY=true + shift + ;; + --claude) + login_claude + LOGIN_ANY=true + shift + ;; + --qwen) + login_qwen + LOGIN_ANY=true + shift + ;; + --iflow) + login_iflow + LOGIN_ANY=true + shift + ;; + --kiro) + login_kiro + LOGIN_ANY=true + shift + ;; + --help|-h) + show_help + exit 0 + ;; + *) + echo_error "Unknown option: $1" + echo "Run 'cliproxyapi-oauth --help' for usage" + exit 1 + ;; + esac +done + +if [ "$LOGIN_ANY" = true ]; then + echo "" + echo_success "OAuth login process complete!" + echo_info "You can now use the models with Factory Droid or other clients." +fi diff --git a/scripts/gui-cliproxyapi.sh b/scripts/gui-cliproxyapi.sh new file mode 100755 index 0000000..89339cb --- /dev/null +++ b/scripts/gui-cliproxyapi.sh @@ -0,0 +1,203 @@ +#!/usr/bin/env bash +# +# CLIProxyAPI-Plus GUI Control Center +# +# Web-based control panel for managing the proxy server + +set -e + +GUI_PORT="${1:-8318}" +NO_BROWSER=false +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --port|-p) + GUI_PORT="$2" + shift 2 + ;; + --no-browser) + NO_BROWSER=true + shift + ;; + --help|-h) + cat << EOF +CLIProxyAPI-Plus GUI Control Center + +Usage: + gui-cliproxyapi [OPTIONS] + +Options: + --port, -p PORT GUI server port (default: 8318) + --no-browser Don't auto-open browser + --help, -h Show this help + +Examples: + gui-cliproxyapi # Start GUI on port 8318 + gui-cliproxyapi --port 9000 # Use custom port +EOF + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +# Colors +GREEN='\033[0;32m' +CYAN='\033[0;36m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +MAGENTA='\033[0;35m' +NC='\033[0m' + +echo_info() { echo -e "${CYAN}[*] $1${NC}"; } +echo_success() { echo -e "${GREEN}[+] $1${NC}"; } +echo_warning() { echo -e "${YELLOW}[!] $1${NC}"; } +echo_error() { echo -e "${RED}[-] $1${NC}"; } + +# Find GUI directory +GUI_DIR="" +if [ -d "$HOME/.local/share/cliproxyapi/gui" ] && [ -f "$HOME/.local/share/cliproxyapi/gui/index.html" ]; then + GUI_DIR="$HOME/.local/share/cliproxyapi/gui" +elif [ -d "$SCRIPT_DIR/../gui" ] && [ -f "$SCRIPT_DIR/../gui/index.html" ]; then + GUI_DIR="$SCRIPT_DIR/../gui" +elif [ -d "/usr/share/cliproxyapi/gui" ] && [ -f "/usr/share/cliproxyapi/gui/index.html" ]; then + GUI_DIR="/usr/share/cliproxyapi/gui" +fi + +if [ -z "$GUI_DIR" ]; then + echo_error "GUI files not found" + echo_info "Creating temporary GUI..." + + # Create minimal GUI + TMP_GUI_DIR=$(mktemp -d) + TMP_GUI="$TMP_GUI_DIR/index.html" + GUI_DIR="$TMP_GUI_DIR" + cat > "$TMP_GUI" << 'EOFHTML' + + + + CLIProxyAPI-Plus Control Center + + + + +

🚀 CLIProxyAPI-Plus Control Center

+
+

Quick Commands

+

Use these commands in your terminal:

+
+# Start server
+start-cliproxyapi --background
+
+# Check status
+start-cliproxyapi --status
+
+# Stop server
+start-cliproxyapi --stop
+
+# Login to providers
+cliproxyapi-oauth --all
+
+# Update
+update-cliproxyapi --use-prebuilt
+        
+
+
+

Server Endpoint

+

http://localhost:8317/v1

+

Use this as your OpenAI API base URL

+
+ + +EOFHTML +fi + +# Check if port is available +if command -v lsof &> /dev/null; then + if lsof -Pi ":$GUI_PORT" -sTCP:LISTEN -t >/dev/null 2>&1; then + echo_warning "Port $GUI_PORT is already in use" + GUI_PORT=$((GUI_PORT + 1)) + echo_info "Using port $GUI_PORT instead" + fi +fi + +# Start simple HTTP server +echo_success "Starting GUI Control Center on port $GUI_PORT" +GUI_URL="http://localhost:$GUI_PORT" + +# Open browser +if [ "$NO_BROWSER" = false ]; then + sleep 1 + if command -v xdg-open &> /dev/null; then + xdg-open "$GUI_URL" 2>/dev/null || true + elif command -v open &> /dev/null; then + open "$GUI_URL" 2>/dev/null || true + elif command -v python3 &> /dev/null; then + python3 -m webbrowser "$GUI_URL" 2>/dev/null || true + fi & +fi + +echo -e "${CYAN}" +cat << EOF +============================================== + GUI Control Center +============================================== + URL: $GUI_URL + Press Ctrl+C to stop +============================================== +EOF +echo -e "${NC}" + +# Start server with API backend +API_SERVER="$HOME/.local/bin/cliproxyapi-api-server" +if command -v python3 &> /dev/null; then + if [ -f "$API_SERVER" ]; then + # Use Python API server with management endpoints + python3 "$API_SERVER" 2>/dev/null + else + # Fallback to simple HTTP server + cd "$GUI_DIR" + python3 -m http.server "$GUI_PORT" 2>/dev/null + fi +elif command -v python &> /dev/null; then + cd "$GUI_DIR" + python -m SimpleHTTPServer "$GUI_PORT" 2>/dev/null +else + echo_error "No HTTP server available (python/python3 required)" + echo_info "Please install Python to use the GUI" + exit 1 +fi diff --git a/scripts/install-cliproxyapi.sh b/scripts/install-cliproxyapi.sh new file mode 100755 index 0000000..3d16fd8 --- /dev/null +++ b/scripts/install-cliproxyapi.sh @@ -0,0 +1,389 @@ +#!/usr/bin/env bash +# +# CLIProxyAPI-Plus Installation Script for Linux/macOS +# +# Complete one-click installer that sets up CLIProxyAPI-Plus for Factory Droid. +# - Clones or downloads pre-built binary +# - Configures ~/.cli-proxy-api/config.yaml +# - Updates ~/.factory/config.json with custom models +# - Provides OAuth login prompts +# +# Author: Auto-generated +# Repo: https://github.com/router-for-me/CLIProxyAPIPlus + +set -e + +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/.local/bin" +CONFIG_DIR="$HOME/.cli-proxy-api" +FACTORY_DIR="$HOME/.factory" +BINARY_NAME="cliproxyapi-plus" + +USE_PREBUILT=false +SKIP_OAUTH=false +FORCE=false + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --use-prebuilt) USE_PREBUILT=true; shift ;; + --skip-oauth) SKIP_OAUTH=true; shift ;; + --force) FORCE=true; shift ;; + *) echo "Unknown option: $1"; exit 1 ;; + esac +done + +# Colors +GREEN='\033[0;32m' +CYAN='\033[0;36m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +MAGENTA='\033[0;35m' +NC='\033[0m' # No Color + +echo_step() { echo -e "\n${CYAN}[*] $1${NC}"; } +echo_success() { echo -e "${GREEN}[+] $1${NC}"; } +echo_warning() { echo -e "${YELLOW}[!] $1${NC}"; } +echo_error() { echo -e "${RED}[-] $1${NC}"; } + +echo -e "${MAGENTA}" +cat << "EOF" +============================================== + CLIProxyAPI-Plus Installer for Droid CLI +============================================== +EOF +echo -e "${NC}" + +# Detect OS and architecture +echo_step "Detecting system..." +OS="$(uname -s | tr '[:upper:]' '[:lower:]')" +ARCH="$(uname -m)" + +case "$ARCH" in + x86_64) ARCH="amd64" ;; + aarch64|arm64) ARCH="arm64" ;; + *) echo_error "Unsupported architecture: $ARCH"; exit 1 ;; +esac + +echo_success "Detected: $OS/$ARCH" + +# Check prerequisites +echo_step "Checking prerequisites..." + +# Check Go (only if not using prebuilt) +if [ "$USE_PREBUILT" = false ]; then + if command -v go &> /dev/null; then + GO_VERSION=$(go version) + echo_success "Go found: $GO_VERSION" + else + echo_warning "Go is not installed. Switching to prebuilt binary mode." + USE_PREBUILT=true + fi +fi + +# Check Git +if command -v git &> /dev/null; then + GIT_VERSION=$(git --version) + echo_success "Git found: $GIT_VERSION" +else + echo_error "Git is not installed. Please install Git first." + exit 1 +fi + +# Check curl or wget +if command -v curl &> /dev/null; then + DOWNLOADER="curl" + echo_success "curl found" +elif command -v wget &> /dev/null; then + DOWNLOADER="wget" + echo_success "wget found" +else + echo_error "Neither curl nor wget is installed. Please install one of them." + exit 1 +fi + +# Create directories +echo_step "Creating directories..." +mkdir -p "$BIN_DIR" "$CONFIG_DIR" "$FACTORY_DIR" +echo_success "Directories ready" + +# Install binary +if [ "$USE_PREBUILT" = true ]; then + echo_step "Downloading pre-built binary from GitHub Releases..." + + # Get latest release info + if [ "$DOWNLOADER" = "curl" ]; then + RELEASE_JSON=$(curl -sL "$RELEASE_API") + else + RELEASE_JSON=$(wget -qO- "$RELEASE_API") + fi + + # Find the appropriate asset + ASSET_NAME="${OS}_${ARCH}.tar.gz" + DOWNLOAD_URL=$(echo "$RELEASE_JSON" | grep -o "https://.*${ASSET_NAME}" | head -1) + + if [ -z "$DOWNLOAD_URL" ]; then + echo_error "Could not find $ASSET_NAME in latest release" + exit 1 + fi + + echo " Downloading $(basename "$DOWNLOAD_URL")..." + TMP_DIR=$(mktemp -d) + TMP_FILE="$TMP_DIR/cliproxyapi-plus.tar.gz" + + if [ "$DOWNLOADER" = "curl" ]; then + curl -sL "$DOWNLOAD_URL" -o "$TMP_FILE" + else + wget -q "$DOWNLOAD_URL" -O "$TMP_FILE" + fi + + echo " Extracting..." + tar -xzf "$TMP_FILE" -C "$TMP_DIR" + + # Find the binary (might be in subdirectory or with different name) + BINARY_PATH=$(find "$TMP_DIR" -type f \( -name "cliproxyapi-plus" -o -name "cli-proxy-api-plus" -o -name "server" \) | head -1) + + if [ -z "$BINARY_PATH" ]; then + echo_error "Could not find binary in extracted archive" + echo " Contents of archive:" + find "$TMP_DIR" -type f + rm -rf "$TMP_DIR" + exit 1 + fi + + cp "$BINARY_PATH" "$BIN_DIR/$BINARY_NAME" + chmod +x "$BIN_DIR/$BINARY_NAME" + rm -rf "$TMP_DIR" + + echo_success "Binary installed: $BIN_DIR/$BINARY_NAME" +else + echo_step "Building from source..." + + # Clone or update repo + if [ -d "$CLONE_DIR" ]; then + if [ "$FORCE" = true ] || [ ! -f "$CLONE_DIR/go.mod" ]; then + echo " Removing existing clone..." + rm -rf "$CLONE_DIR" + echo " Cloning repository..." + git clone --depth 1 "$REPO_URL" "$CLONE_DIR" + fi + else + echo " Cloning repository..." + git clone --depth 1 "$REPO_URL" "$CLONE_DIR" + fi + + echo " Building binary..." + cd "$CLONE_DIR" + go build -o "$BIN_DIR/$BINARY_NAME" ./cmd/server + cd - > /dev/null + + chmod +x "$BIN_DIR/$BINARY_NAME" + echo_success "Binary built: $BIN_DIR/$BINARY_NAME" +fi + +# Create config.yaml +echo_step "Configuring ~/.cli-proxy-api/config.yaml..." +CONFIG_PATH="$CONFIG_DIR/config.yaml" + +if [ -f "$CONFIG_PATH" ] && [ "$FORCE" = false ]; then + echo_warning "config.yaml already exists, skipping (use --force to overwrite)" +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 + echo_success "config.yaml created" +fi + +# Update .factory/config.json +echo_step "Updating ~/.factory/config.json..." +FACTORY_CONFIG="$FACTORY_DIR/config.json" + +cat > "$FACTORY_CONFIG" << '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 Thinking [Antigravity]", "model": "gemini-claude-sonnet-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 3 Pro [Antigravity]", "model": "gemini-3-pro-preview", "base_url": "http://localhost:8317/v1", "api_key": "sk-dummy", "provider": "openai" }, + { "model_display_name": "GPT OSS 120B [Antigravity]", "model": "gpt-oss-120b-medium", "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 Mini [Copilot]", "model": "gpt-5-mini", "base_url": "http://localhost:8317/v1", "api_key": "sk-dummy", "provider": "openai" }, + { "model_display_name": "Grok Code Fast 1 [Copilot]", "model": "grok-code-fast-1", "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": "Gemini 3 Pro Preview [Gemini]", "model": "gemini-3-pro-preview", "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": "GLM 4.6 [iFlow]", "model": "glm-4.6", "base_url": "http://localhost:8317/v1", "api_key": "sk-dummy", "provider": "openai" }, + { "model_display_name": "Minimax M2 [iFlow]", "model": "minimax-m2", "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" }, + { "model_display_name": "Claude Sonnet 4.5 [Kiro]", "model": "kiro-claude-sonnet-4.5", "base_url": "http://localhost:8317/v1", "api_key": "sk-dummy", "provider": "openai" }, + { "model_display_name": "Claude Sonnet 4 [Kiro]", "model": "kiro-claude-sonnet-4", "base_url": "http://localhost:8317/v1", "api_key": "sk-dummy", "provider": "openai" }, + { "model_display_name": "Claude Haiku 4.5 [Kiro]", "model": "kiro-claude-haiku-4.5", "base_url": "http://localhost:8317/v1", "api_key": "sk-dummy", "provider": "openai" } + ] +} +EOF + +MODEL_COUNT=$(grep -c "model_display_name" "$FACTORY_CONFIG") +echo_success "config.json updated with $MODEL_COUNT custom models" + +# Verify installation +echo_step "Verifying installation..." +if [ -f "$BIN_DIR/$BINARY_NAME" ]; then + FILE_SIZE=$(du -h "$BIN_DIR/$BINARY_NAME" | cut -f1) + echo_success "Binary verification passed ($FILE_SIZE)" +else + echo_error "Binary not found at $BIN_DIR/$BINARY_NAME" + exit 1 +fi + +# Add ~/.local/bin to PATH if not already +echo_step "Configuring PATH..." +SHELL_RC="" +if [ -n "$BASH_VERSION" ]; then + SHELL_RC="$HOME/.bashrc" +elif [ -n "$ZSH_VERSION" ]; then + SHELL_RC="$HOME/.zshrc" +else + SHELL_RC="$HOME/.profile" +fi + +PATH_EXPORT="export PATH=\"\$HOME/.local/bin:\$PATH\"" + +if grep -q ".local/bin" "$SHELL_RC" 2>/dev/null; then + echo_success "$BIN_DIR already in PATH" + PATH_ADDED=false +else + echo "" >> "$SHELL_RC" + echo "# Added by CLIProxyAPI-Plus installer" >> "$SHELL_RC" + echo "$PATH_EXPORT" >> "$SHELL_RC" + echo_success "Added $BIN_DIR to PATH in $SHELL_RC" + PATH_ADDED=true +fi + +# Copy helper scripts +echo_step "Installing helper scripts..." +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +for script in start-cliproxyapi.sh cliproxyapi-oauth.sh update-cliproxyapi.sh uninstall-cliproxyapi.sh gui-cliproxyapi.sh; do + if [ -f "$SCRIPT_DIR/$script" ]; then + SCRIPT_BASENAME=$(basename "$script" .sh) + cp "$SCRIPT_DIR/$script" "$BIN_DIR/$SCRIPT_BASENAME" + chmod +x "$BIN_DIR/$SCRIPT_BASENAME" + echo_success "Installed: $SCRIPT_BASENAME" + fi +done + +# Copy Python API server for GUI +if [ -f "$SCRIPT_DIR/cliproxyapi-api-server.py" ]; then + cp "$SCRIPT_DIR/cliproxyapi-api-server.py" "$BIN_DIR/cliproxyapi-api-server" + chmod +x "$BIN_DIR/cliproxyapi-api-server" + echo_success "Installed: API server for GUI" +fi + +# Copy GUI files +GUI_INSTALL_DIR="$HOME/.local/share/cliproxyapi/gui" +GUI_SOURCE_DIR="$(dirname "$SCRIPT_DIR")/gui" + +if [ -d "$GUI_SOURCE_DIR" ]; then + echo_step "Installing GUI Control Center..." + mkdir -p "$GUI_INSTALL_DIR" + cp -r "$GUI_SOURCE_DIR"/* "$GUI_INSTALL_DIR/" + echo_success "GUI installed: $GUI_INSTALL_DIR" +fi + +# OAuth login prompts +if [ "$SKIP_OAUTH" = false ]; then + echo -e "\n${YELLOW}" + cat << EOF +============================================== + OAuth Login Setup (Optional) +============================================== +Run these commands to login to each provider: + + # Gemini CLI + cliproxyapi-plus --config $CONFIG_DIR/config.yaml --login + + # Antigravity + cliproxyapi-plus --config $CONFIG_DIR/config.yaml --antigravity-login + + # GitHub Copilot + cliproxyapi-plus --config $CONFIG_DIR/config.yaml --github-copilot-login + + # Codex + cliproxyapi-plus --config $CONFIG_DIR/config.yaml --codex-login + + # Claude + cliproxyapi-plus --config $CONFIG_DIR/config.yaml --claude-login + + # Qwen + cliproxyapi-plus --config $CONFIG_DIR/config.yaml --qwen-login + + # iFlow + cliproxyapi-plus --config $CONFIG_DIR/config.yaml --iflow-login + + # Kiro (AWS) + cliproxyapi-plus --config $CONFIG_DIR/config.yaml --kiro-aws-login + +============================================== +EOF + echo -e "${NC}" +fi + +echo -e "\n${GREEN}" +cat << EOF +============================================== + Installation Complete! +============================================== +EOF +echo -e "${NC}" + +echo -e "${CYAN}" +cat << EOF +Installed Files: + Binary: $BIN_DIR/$BINARY_NAME + Config: $CONFIG_DIR/config.yaml + Droid: $FACTORY_DIR/config.json + +Available Scripts (in $BIN_DIR): + start-cliproxyapi Start/stop/restart server + cliproxyapi-oauth Login to OAuth providers + gui-cliproxyapi Open Control Center GUI + update-cliproxyapi Update to latest version + uninstall-cliproxyapi Remove everything + +Quick Start: + 1. Reload shell: source $SHELL_RC + 2. Start server: start-cliproxyapi --background + 3. Login OAuth: cliproxyapi-oauth --all + 4. Open GUI: gui-cliproxyapi + 5. Use with Droid: droid (select cliproxyapi-plus/* model) +EOF +echo -e "${NC}" + +if [ "$PATH_ADDED" = true ]; then + echo -e "${YELLOW}" + cat << EOF + +NOTE: Restart your terminal or run: source $SHELL_RC + to apply PATH changes. +EOF + echo -e "${NC}" +fi + +echo -e "${GREEN}" +echo "==============================================" +echo -e "${NC}" diff --git a/scripts/start-cliproxyapi.sh b/scripts/start-cliproxyapi.sh new file mode 100755 index 0000000..37f5a01 --- /dev/null +++ b/scripts/start-cliproxyapi.sh @@ -0,0 +1,207 @@ +#!/usr/bin/env bash +# +# CLIProxyAPI-Plus Server Manager +# +# Start, stop, restart, and monitor the proxy server + +set -e + +BINARY_PATH="$HOME/.local/bin/cliproxyapi-plus" +CONFIG_PATH="$HOME/.cli-proxy-api/config.yaml" +PID_FILE="$HOME/.cli-proxy-api/server.pid" +LOG_FILE="$HOME/.cli-proxy-api/server.log" + +# Colors +GREEN='\033[0;32m' +CYAN='\033[0;36m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +NC='\033[0m' + +echo_info() { echo -e "${CYAN}[*] $1${NC}"; } +echo_success() { echo -e "${GREEN}[+] $1${NC}"; } +echo_warning() { echo -e "${YELLOW}[!] $1${NC}"; } +echo_error() { echo -e "${RED}[-] $1${NC}"; } + +# Check if server is running +is_running() { + if [ -f "$PID_FILE" ]; then + PID=$(cat "$PID_FILE") + if ps -p "$PID" > /dev/null 2>&1; then + return 0 + else + rm -f "$PID_FILE" + return 1 + fi + fi + return 1 +} + +# Start server +start_server() { + if is_running; then + echo_warning "Server is already running (PID: $(cat "$PID_FILE"))" + return 0 + fi + + if [ ! -f "$BINARY_PATH" ]; then + echo_error "Binary not found: $BINARY_PATH" + echo_error "Run install-cliproxyapi first" + exit 1 + fi + + if [ ! -f "$CONFIG_PATH" ]; then + echo_error "Config not found: $CONFIG_PATH" + echo_error "Run install-cliproxyapi first" + exit 1 + fi + + if [ "$1" = "--background" ]; then + echo_info "Starting server in background..." + nohup "$BINARY_PATH" --config "$CONFIG_PATH" > "$LOG_FILE" 2>&1 & + PID=$! + echo $PID > "$PID_FILE" + sleep 1 + + if is_running; then + echo_success "Server started (PID: $PID)" + echo_info "Log: $LOG_FILE" + echo_info "Endpoint: http://localhost:8317/v1" + else + echo_error "Server failed to start. Check logs: $LOG_FILE" + exit 1 + fi + else + echo_info "Starting server in foreground..." + echo_info "Press Ctrl+C to stop" + "$BINARY_PATH" --config "$CONFIG_PATH" + fi +} + +# Stop server +stop_server() { + if ! is_running; then + echo_warning "Server is not running" + return 0 + fi + + PID=$(cat "$PID_FILE") + echo_info "Stopping server (PID: $PID)..." + kill "$PID" 2>/dev/null || true + sleep 1 + + if is_running; then + echo_warning "Server didn't stop gracefully, forcing..." + kill -9 "$PID" 2>/dev/null || true + sleep 1 + fi + + rm -f "$PID_FILE" + + if is_running; then + echo_error "Failed to stop server" + exit 1 + else + echo_success "Server stopped" + fi +} + +# Restart server +restart_server() { + echo_info "Restarting server..." + stop_server + sleep 1 + start_server --background +} + +# Show status +show_status() { + if is_running; then + PID=$(cat "$PID_FILE") + echo_success "Server is running (PID: $PID)" + echo_info "Endpoint: http://localhost:8317/v1" + + if [ -f "$LOG_FILE" ]; then + echo_info "Log file: $LOG_FILE" + fi + + # Try to get process info + if command -v ps &> /dev/null; then + PS_INFO=$(ps -p "$PID" -o comm= 2>/dev/null || echo "N/A") + echo_info "Process: $PS_INFO" + fi + else + echo_warning "Server is not running" + fi +} + +# Show logs +show_logs() { + if [ ! -f "$LOG_FILE" ]; then + echo_error "Log file not found: $LOG_FILE" + exit 1 + fi + + if [ "$1" = "--follow" ] || [ "$1" = "-f" ]; then + tail -f "$LOG_FILE" + else + tail -n 50 "$LOG_FILE" + fi +} + +# Show help +show_help() { + cat << EOF +CLIProxyAPI-Plus Server Manager + +Usage: + start-cliproxyapi [OPTIONS] + +Options: + --background, -b Start server in background + --stop, -s Stop server + --restart, -r Restart server + --status Show server status + --logs [--follow] Show server logs (use --follow to tail) + --help, -h Show this help + +Examples: + start-cliproxyapi --background # Start in background + start-cliproxyapi # Start in foreground + start-cliproxyapi --stop # Stop server + start-cliproxyapi --restart # Restart server + start-cliproxyapi --status # Check status + start-cliproxyapi --logs # Show last 50 log lines + start-cliproxyapi --logs --follow # Tail logs +EOF +} + +# Parse arguments +case "${1:-}" in + --background|-b) + start_server --background + ;; + --stop|-s) + stop_server + ;; + --restart|-r) + restart_server + ;; + --status) + show_status + ;; + --logs) + show_logs "$2" + ;; + --help|-h) + show_help + ;; + "") + start_server + ;; + *) + echo_error "Unknown option: $1" + echo "Run 'start-cliproxyapi --help' for usage" + exit 1 + ;; +esac diff --git a/scripts/uninstall-cliproxyapi.sh b/scripts/uninstall-cliproxyapi.sh new file mode 100755 index 0000000..7054214 --- /dev/null +++ b/scripts/uninstall-cliproxyapi.sh @@ -0,0 +1,164 @@ +#!/usr/bin/env bash +# +# CLIProxyAPI-Plus Uninstall Script +# +# Remove all installed files and optionally auth tokens + +set -e + +BIN_DIR="$HOME/.local/bin" +CONFIG_DIR="$HOME/.cli-proxy-api" +FACTORY_DIR="$HOME/.factory" +CLONE_DIR="$HOME/CLIProxyAPIPlus" +BINARY_NAME="cliproxyapi-plus" + +REMOVE_ALL=false +FORCE=false + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --all) REMOVE_ALL=true; shift ;; + --force) FORCE=true; shift ;; + *) echo "Unknown option: $1"; exit 1 ;; + esac +done + +# Colors +GREEN='\033[0;32m' +CYAN='\033[0;36m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +MAGENTA='\033[0;35m' +NC='\033[0m' + +echo_step() { echo -e "\n${CYAN}[*] $1${NC}"; } +echo_success() { echo -e "${GREEN}[+] $1${NC}"; } +echo_warning() { echo -e "${YELLOW}[!] $1${NC}"; } +echo_error() { echo -e "${RED}[-] $1${NC}"; } + +echo -e "${MAGENTA}" +cat << "EOF" +============================================== + CLIProxyAPI-Plus Uninstall Script +============================================== +EOF +echo -e "${NC}" + +# Confirmation prompt +if [ "$FORCE" = false ]; then + echo_warning "This will remove CLIProxyAPI-Plus from your system." + if [ "$REMOVE_ALL" = true ]; then + echo_warning "Including auth tokens and configuration files!" + else + echo_info "Auth tokens will be preserved (use --all to remove them)" + fi + echo "" + read -p "Continue? [y/N] " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo_info "Uninstall cancelled" + exit 0 + fi +fi + +# Stop server if running +PID_FILE="$CONFIG_DIR/server.pid" +if [ -f "$PID_FILE" ]; then + PID=$(cat "$PID_FILE") + if ps -p "$PID" > /dev/null 2>&1; then + echo_step "Stopping server..." + kill "$PID" 2>/dev/null || true + sleep 1 + if ps -p "$PID" > /dev/null 2>&1; then + kill -9 "$PID" 2>/dev/null || true + fi + echo_success "Server stopped" + fi +fi + +# Remove binary +echo_step "Removing binary..." +if [ -f "$BIN_DIR/$BINARY_NAME" ]; then + rm -f "$BIN_DIR/$BINARY_NAME" + echo_success "Removed: $BIN_DIR/$BINARY_NAME" +else + echo_warning "Binary not found (already removed?)" +fi + +# Remove helper scripts +echo_step "Removing helper scripts..." +for script in start-cliproxyapi cliproxyapi-oauth gui-cliproxyapi update-cliproxyapi uninstall-cliproxyapi; do + if [ -f "$BIN_DIR/$script" ]; then + rm -f "$BIN_DIR/$script" + echo_success "Removed: $script" + fi +done + +# Remove source directory +if [ -d "$CLONE_DIR" ]; then + echo_step "Removing source directory..." + rm -rf "$CLONE_DIR" + echo_success "Removed: $CLONE_DIR" +fi + +# Remove config and auth +if [ "$REMOVE_ALL" = true ]; then + echo_step "Removing configuration and auth tokens..." + if [ -d "$CONFIG_DIR" ]; then + rm -rf "$CONFIG_DIR" + echo_success "Removed: $CONFIG_DIR" + fi + + # Remove Droid custom models + if [ -f "$FACTORY_DIR/config.json" ]; then + echo_step "Removing Droid custom models..." + rm -f "$FACTORY_DIR/config.json" + echo_success "Removed: $FACTORY_DIR/config.json" + fi +else + echo_step "Preserving configuration and auth tokens..." + echo_info "Config: $CONFIG_DIR" + echo_info "Use --all flag to remove them" +fi + +# Clean up PATH entry +echo_step "Checking PATH configuration..." +SHELL_RC="" +if [ -n "$BASH_VERSION" ]; then + SHELL_RC="$HOME/.bashrc" +elif [ -n "$ZSH_VERSION" ]; then + SHELL_RC="$HOME/.zshrc" +else + SHELL_RC="$HOME/.profile" +fi + +if [ -f "$SHELL_RC" ]; then + if grep -q "CLIProxyAPI-Plus installer" "$SHELL_RC" 2>/dev/null; then + echo_warning "Found PATH entry in $SHELL_RC" + echo_info "You may want to manually remove the following lines:" + echo "" + grep -A1 "CLIProxyAPI-Plus installer" "$SHELL_RC" || true + echo "" + fi +fi + +echo "" +echo -e "${GREEN}" +cat << "EOF" +============================================== + Uninstall Complete! +============================================== +EOF +echo -e "${NC}" + +if [ "$REMOVE_ALL" = true ]; then + echo_success "All CLIProxyAPI-Plus files have been removed" +else + echo_success "CLIProxyAPI-Plus binary removed" + echo_info "Configuration and auth tokens preserved at: $CONFIG_DIR" + echo_info "Run with --all flag to remove everything" +fi + +echo "" +echo_info "Thank you for using CLIProxyAPI-Plus!" diff --git a/scripts/update-cliproxyapi.sh b/scripts/update-cliproxyapi.sh new file mode 100755 index 0000000..7156081 --- /dev/null +++ b/scripts/update-cliproxyapi.sh @@ -0,0 +1,234 @@ +#!/usr/bin/env bash +# +# CLIProxyAPI-Plus Update Script +# +# Update to the latest version from GitHub + +set -e + +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/.local/bin" +BINARY_NAME="cliproxyapi-plus" + +USE_PREBUILT=false +FORCE=false + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --use-prebuilt) USE_PREBUILT=true; shift ;; + --force) FORCE=true; shift ;; + *) echo "Unknown option: $1"; exit 1 ;; + esac +done + +# Colors +GREEN='\033[0;32m' +CYAN='\033[0;36m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +MAGENTA='\033[0;35m' +NC='\033[0m' + +echo_step() { echo -e "\n${CYAN}[*] $1${NC}"; } +echo_success() { echo -e "${GREEN}[+] $1${NC}"; } +echo_warning() { echo -e "${YELLOW}[!] $1${NC}"; } +echo_error() { echo -e "${RED}[-] $1${NC}"; } + +echo -e "${MAGENTA}" +cat << "EOF" +============================================== + CLIProxyAPI-Plus Update Script +============================================== +EOF +echo -e "${NC}" + +# Detect OS and architecture +OS="$(uname -s | tr '[:upper:]' '[:lower:]')" +ARCH="$(uname -m)" + +case "$ARCH" in + x86_64) ARCH="amd64" ;; + aarch64|arm64) ARCH="arm64" ;; + *) echo_error "Unsupported architecture: $ARCH"; exit 1 ;; +esac + +# Check if currently installed +if [ ! -f "$BIN_DIR/$BINARY_NAME" ]; then + echo_error "CLIProxyAPI-Plus is not installed" + echo_info "Run install-cliproxyapi first" + exit 1 +fi + +# Get current version +CURRENT_VERSION="unknown" +if [ -f "$BIN_DIR/$BINARY_NAME" ]; then + CURRENT_VERSION=$("$BIN_DIR/$BINARY_NAME" --version 2>&1 | head -1 || echo "unknown") +fi +echo_info "Current version: $CURRENT_VERSION" + +# Check if server is running +PID_FILE="$HOME/.cli-proxy-api/server.pid" +SERVER_WAS_RUNNING=false +if [ -f "$PID_FILE" ]; then + PID=$(cat "$PID_FILE") + if ps -p "$PID" > /dev/null 2>&1; then + SERVER_WAS_RUNNING=true + echo_warning "Server is running. Stopping it first..." + if command -v start-cliproxyapi &> /dev/null; then + start-cliproxyapi --stop + else + kill "$PID" 2>/dev/null || true + rm -f "$PID_FILE" + fi + sleep 1 + fi +fi + +# Update method +if [ "$USE_PREBUILT" = true ] || [ ! -d "$CLONE_DIR" ]; then + echo_step "Downloading latest pre-built binary..." + + # Check for curl or wget + if command -v curl &> /dev/null; then + DOWNLOADER="curl" + elif command -v wget &> /dev/null; then + DOWNLOADER="wget" + else + echo_error "Neither curl nor wget is installed" + exit 1 + fi + + # Get latest release info + if [ "$DOWNLOADER" = "curl" ]; then + RELEASE_JSON=$(curl -sL "$RELEASE_API") + else + RELEASE_JSON=$(wget -qO- "$RELEASE_API") + fi + + # Extract version + LATEST_VERSION=$(echo "$RELEASE_JSON" | grep -o '"tag_name": *"[^"]*"' | sed 's/"tag_name": *"\(.*\)"/\1/') + echo_info "Latest version: $LATEST_VERSION" + + if [ "$CURRENT_VERSION" = "$LATEST_VERSION" ] && [ "$FORCE" = false ]; then + echo_success "Already up to date!" + exit 0 + fi + + # Find the appropriate asset + ASSET_NAME="${OS}_${ARCH}.tar.gz" + DOWNLOAD_URL=$(echo "$RELEASE_JSON" | grep -o "https://.*${ASSET_NAME}" | head -1) + + if [ -z "$DOWNLOAD_URL" ]; then + echo_error "Could not find $ASSET_NAME in latest release" + exit 1 + fi + + echo " Downloading $(basename "$DOWNLOAD_URL")..." + TMP_DIR=$(mktemp -d) + TMP_FILE="$TMP_DIR/cliproxyapi-plus.tar.gz" + + if [ "$DOWNLOADER" = "curl" ]; then + curl -sL "$DOWNLOAD_URL" -o "$TMP_FILE" + else + wget -q "$DOWNLOAD_URL" -O "$TMP_FILE" + fi + + echo " Extracting..." + tar -xzf "$TMP_FILE" -C "$TMP_DIR" + + # Find the binary (might have different name) + BINARY_PATH=$(find "$TMP_DIR" -type f \( -name "cliproxyapi-plus" -o -name "cli-proxy-api-plus" -o -name "server" \) | head -1) + + if [ -z "$BINARY_PATH" ]; then + echo_error "Could not find binary in extracted archive" + rm -rf "$TMP_DIR" + exit 1 + fi + + # Backup old binary + if [ -f "$BIN_DIR/$BINARY_NAME" ]; then + cp "$BIN_DIR/$BINARY_NAME" "$BIN_DIR/$BINARY_NAME.backup" + fi + + cp "$BINARY_PATH" "$BIN_DIR/$BINARY_NAME" + chmod +x "$BIN_DIR/$BINARY_NAME" + rm -rf "$TMP_DIR" + + echo_success "Binary updated to $LATEST_VERSION" +else + echo_step "Updating from source..." + + # Check Go + if ! command -v go &> /dev/null; then + echo_error "Go is not installed. Use --use-prebuilt flag instead." + exit 1 + fi + + if [ ! -d "$CLONE_DIR" ]; then + echo_error "Source directory not found: $CLONE_DIR" + echo_info "Use --use-prebuilt flag to download binary" + exit 1 + fi + + cd "$CLONE_DIR" + + # Get current commit + OLD_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") + + # Pull latest changes + echo " Pulling latest changes..." + git fetch origin + git reset --hard origin/main + + # Get new commit + NEW_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") + + if [ "$OLD_COMMIT" = "$NEW_COMMIT" ] && [ "$FORCE" = false ]; then + echo_success "Already up to date! (commit: $NEW_COMMIT)" + cd - > /dev/null + exit 0 + fi + + echo_info "Updating from $OLD_COMMIT to $NEW_COMMIT" + + # Build + echo " Building binary..." + + # Backup old binary + if [ -f "$BIN_DIR/$BINARY_NAME" ]; then + cp "$BIN_DIR/$BINARY_NAME" "$BIN_DIR/$BINARY_NAME.backup" + fi + + go build -o "$BIN_DIR/$BINARY_NAME" ./cmd/server + chmod +x "$BIN_DIR/$BINARY_NAME" + + cd - > /dev/null + echo_success "Binary updated to commit $NEW_COMMIT" +fi + +# Verify new version +NEW_VERSION=$("$BIN_DIR/$BINARY_NAME" --version 2>&1 | head -1 || echo "installed") +echo_success "New version: $NEW_VERSION" + +# Restart server if it was running +if [ "$SERVER_WAS_RUNNING" = true ]; then + echo_step "Restarting server..." + if command -v start-cliproxyapi &> /dev/null; then + start-cliproxyapi --background + echo_success "Server restarted" + else + echo_warning "Server was not restarted automatically" + echo_info "Run 'start-cliproxyapi --background' to start it" + fi +fi + +echo "" +echo_success "Update complete!" + +if [ -f "$BIN_DIR/$BINARY_NAME.backup" ]; then + echo_info "Backup saved to: $BIN_DIR/$BINARY_NAME.backup" + echo_info "Run 'rm $BIN_DIR/$BINARY_NAME.backup' to remove it" +fi