Last active
January 21, 2026 14:20
-
-
Save KNN-07/79e3ae566ebc8e3df20686ea20628e1c to your computer and use it in GitHub Desktop.
Migration
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| Antigravity Manager for CLIProxyAPI | |
| A comprehensive tool to: | |
| 1. Convert OpenCode Antigravity accounts to CLIProxyAPI format | |
| 2. Refresh access tokens using Google OAuth2 | |
| Usage: | |
| python antigravity-manager.py convert # Convert antigravity accounts | |
| python antigravity-manager.py refresh # Refresh access tokens | |
| python antigravity-manager.py all # Convert then refresh (default) | |
| """ | |
| import json | |
| import sys | |
| import requests | |
| from pathlib import Path | |
| from datetime import datetime, timedelta | |
| # ============================================================================ | |
| # Configuration | |
| # ============================================================================ | |
| # OAuth2 credentials for Antigravity | |
| ANTIGRAVITY_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com" | |
| ANTIGRAVITY_CLIENT_SECRET = "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" | |
| TOKEN_URL = "https://oauth2.googleapis.com/token" | |
| # Paths | |
| ANTIGRAVITY_FILE = Path.home() / ".config/opencode/antigravity-accounts.json" | |
| CLIPROXYAPI_AUTH_DIR = Path.home() / ".cli-proxy-api" | |
| CLIPROXYAPI_DIR = Path.home() / "cliproxyapi" | |
| # ============================================================================ | |
| # Colored Output | |
| # ============================================================================ | |
| class Colors: | |
| GREEN = '\033[0;32m' | |
| BLUE = '\033[0;34m' | |
| YELLOW = '\033[1;33m' | |
| RED = '\033[0;31m' | |
| CYAN = '\033[0;36m' | |
| MAGENTA = '\033[0;35m' | |
| BOLD = '\033[1m' | |
| NC = '\033[0m' # No Color | |
| def log_info(msg): | |
| print(f"{Colors.BLUE}[INFO]{Colors.NC} {msg}") | |
| def log_success(msg): | |
| print(f"{Colors.GREEN}[SUCCESS]{Colors.NC} {msg}") | |
| def log_warning(msg): | |
| print(f"{Colors.YELLOW}[WARNING]{Colors.NC} {msg}") | |
| def log_error(msg): | |
| print(f"{Colors.RED}[ERROR]{Colors.NC} {msg}") | |
| def print_header(title): | |
| width = 61 | |
| print(f"\n{Colors.CYAN}{'=' * width}{Colors.NC}") | |
| print(f"{Colors.CYAN}{Colors.BOLD} {title}{Colors.NC}") | |
| print(f"{Colors.CYAN}{'=' * width}{Colors.NC}\n") | |
| def print_section(title): | |
| print(f"\n{Colors.MAGENTA}── {title} ──{Colors.NC}\n") | |
| # ============================================================================ | |
| # Token Refresh Functions | |
| # ============================================================================ | |
| def refresh_token(refresh_token_value): | |
| """Refresh an access token using the refresh token via Google OAuth2""" | |
| form_data = { | |
| "client_id": ANTIGRAVITY_CLIENT_ID, | |
| "client_secret": ANTIGRAVITY_CLIENT_SECRET, | |
| "grant_type": "refresh_token", | |
| "refresh_token": refresh_token_value | |
| } | |
| headers = { | |
| "Host": "oauth2.googleapis.com", | |
| "User-Agent": "Mozilla/5.0", | |
| "Content-Type": "application/x-www-form-urlencoded" | |
| } | |
| response = requests.post(TOKEN_URL, data=form_data, headers=headers) | |
| if response.status_code != 200: | |
| raise Exception(f"Token refresh failed: {response.status_code} - {response.text}") | |
| return response.json() | |
| def update_credential_file(file_path, token_response): | |
| """Update credential file with new tokens""" | |
| with open(file_path, 'r') as f: | |
| cred = json.load(f) | |
| now = datetime.now() | |
| expires_in = token_response.get('expires_in', 3600) | |
| cred['access_token'] = token_response['access_token'] | |
| if token_response.get('refresh_token'): | |
| cred['refresh_token'] = token_response['refresh_token'] | |
| cred['expires_in'] = expires_in | |
| cred['timestamp'] = int(now.timestamp() * 1000) | |
| cred['expired'] = (now + timedelta(seconds=expires_in)).isoformat() + 'Z' | |
| with open(file_path, 'w') as f: | |
| json.dump(cred, f, indent=2) | |
| return cred | |
| # ============================================================================ | |
| # Convert Command | |
| # ============================================================================ | |
| def cmd_convert(): | |
| """Convert OpenCode Antigravity accounts to CLIProxyAPI format""" | |
| print_header("Antigravity -> CLIProxyAPI Converter") | |
| # Check antigravity file | |
| if not ANTIGRAVITY_FILE.exists(): | |
| log_error(f"Antigravity accounts file not found: {ANTIGRAVITY_FILE}") | |
| return 1, [] | |
| log_success(f"Found antigravity accounts: {ANTIGRAVITY_FILE}") | |
| # Read antigravity accounts | |
| with open(ANTIGRAVITY_FILE, 'r') as f: | |
| data = json.load(f) | |
| accounts = data.get('accounts', []) | |
| log_info(f"Found {len(accounts)} account(s)") | |
| if not accounts: | |
| log_warning("No accounts found in antigravity file") | |
| return 0, [] | |
| # Create auth directory | |
| CLIPROXYAPI_AUTH_DIR.mkdir(parents=True, exist_ok=True) | |
| log_success(f"Auth directory ready: {CLIPROXYAPI_AUTH_DIR}") | |
| print_section("Processing Accounts") | |
| # Process each account | |
| created_files = [] | |
| for i, account in enumerate(accounts, 1): | |
| email = account.get('email', f'unknown_{i}') | |
| refresh_token_val = account.get('refreshToken') | |
| project_id = account.get('projectId') or account.get('managedProjectId') | |
| if not refresh_token_val: | |
| log_warning(f"Account {i} ({email}): No refresh token, skipping") | |
| continue | |
| print(f"{Colors.YELLOW}Account {i}: {email}{Colors.NC}") | |
| now = datetime.now() | |
| expires_in = 3600 | |
| timestamp = int(now.timestamp() * 1000) | |
| expired = (now + timedelta(seconds=expires_in)).isoformat() + 'Z' | |
| credential = { | |
| "type": "antigravity", | |
| "email": email, | |
| "refresh_token": refresh_token_val, | |
| "access_token": "", | |
| "expires_in": expires_in, | |
| "timestamp": timestamp, | |
| "expired": expired | |
| } | |
| if project_id: | |
| credential["project_id"] = project_id | |
| print(f" Project: {project_id}") | |
| # Save credential file with antigravity naming convention | |
| # CLIProxyAPI expects: antigravity-<email_sanitized>.json | |
| safe_email = email.replace('@', '_').replace('.', '_') | |
| cred_file = CLIPROXYAPI_AUTH_DIR / f"antigravity-{safe_email}.json" | |
| with open(cred_file, 'w') as f: | |
| json.dump(credential, f, indent=2) | |
| created_files.append(cred_file) | |
| log_success(f"Created: {cred_file.name}") | |
| print() | |
| # Summary | |
| print(f"{Colors.CYAN}{'=' * 61}{Colors.NC}") | |
| print(f"{Colors.GREEN}{Colors.BOLD} Conversion Complete!{Colors.NC}") | |
| print(f"{Colors.CYAN}{'=' * 61}{Colors.NC}\n") | |
| print(f"{Colors.BLUE}Created {len(created_files)} credential file(s):{Colors.NC}") | |
| for cf in created_files: | |
| print(f" * {cf}") | |
| return 0, created_files | |
| # ============================================================================ | |
| # Refresh Command | |
| # ============================================================================ | |
| def cmd_refresh(): | |
| """Refresh access tokens for all antigravity credential files""" | |
| print_header("Antigravity Token Refresher") | |
| auth_files = list(CLIPROXYAPI_AUTH_DIR.glob("antigravity-*.json")) | |
| if not auth_files: | |
| log_error(f"No antigravity credential files found in {CLIPROXYAPI_AUTH_DIR}") | |
| log_info("Run 'convert' first to create credential files") | |
| return 1 | |
| log_info(f"Found {len(auth_files)} credential file(s)") | |
| print_section("Refreshing Tokens") | |
| success_count = 0 | |
| fail_count = 0 | |
| for file_path in auth_files: | |
| try: | |
| with open(file_path, 'r') as f: | |
| cred = json.load(f) | |
| email = cred.get('email', 'unknown') | |
| refresh_token_value = cred.get('refresh_token') | |
| if not refresh_token_value: | |
| log_warning(f"{email}: No refresh token, skipping") | |
| fail_count += 1 | |
| continue | |
| print(f"{Colors.YELLOW}{email}{Colors.NC}...", end=" ", flush=True) | |
| token_response = refresh_token(refresh_token_value) | |
| updated_cred = update_credential_file(file_path, token_response) | |
| print(f"{Colors.GREEN}OK{Colors.NC}") | |
| print(f" Access token: {updated_cred['access_token'][:30]}...") | |
| print(f" Expires: {updated_cred['expired']}") | |
| print() | |
| success_count += 1 | |
| except Exception as e: | |
| print(f"{Colors.RED}FAILED{Colors.NC}") | |
| log_error(f" {str(e)}") | |
| print() | |
| fail_count += 1 | |
| # Summary | |
| print(f"{Colors.CYAN}{'=' * 61}{Colors.NC}") | |
| print(f"{Colors.GREEN} Success: {success_count}{Colors.NC}") | |
| if fail_count > 0: | |
| print(f"{Colors.RED} Failed: {fail_count}{Colors.NC}") | |
| print(f"{Colors.CYAN}{'=' * 61}{Colors.NC}\n") | |
| return 0 if fail_count == 0 else 1 | |
| # ============================================================================ | |
| # Help & Next Steps | |
| # ============================================================================ | |
| def print_next_steps(): | |
| """Print next steps for CLIProxyAPI setup""" | |
| print_section("Next Steps") | |
| step = 1 | |
| # Check if CLIProxyAPI is installed | |
| if not CLIPROXYAPI_DIR.exists(): | |
| print(f"{Colors.YELLOW}{step}. Install CLIProxyAPI:{Colors.NC}") | |
| print(f" ./cliproxyapi-installer install") | |
| print() | |
| step += 1 | |
| print(f"{Colors.YELLOW}{step}. Ensure your config.yaml has the correct auth-dir:{Colors.NC}") | |
| print(f" {Colors.CYAN}auth-dir: \"~/.cli-proxy-api\"{Colors.NC}") | |
| print() | |
| step += 1 | |
| print(f"{Colors.YELLOW}{step}. Start CLIProxyAPI:{Colors.NC}") | |
| print(f" {Colors.CYAN}cd ~/cliproxyapi && ./cli-proxy-api{Colors.NC}") | |
| print() | |
| step += 1 | |
| print(f"{Colors.YELLOW}{step}. Test Gemini access:{Colors.NC}") | |
| print(f" {Colors.CYAN}curl -X POST http://localhost:8317/v1/chat/completions \\{Colors.NC}") | |
| print(f" {Colors.CYAN} -H 'Authorization: Bearer YOUR-API-KEY' \\{Colors.NC}") | |
| print(f" {Colors.CYAN} -H 'Content-Type: application/json' \\{Colors.NC}") | |
| print(f" {Colors.CYAN} -d '{{\"model\": \"gemini-2.0-flash-exp\", \"messages\": [{{\"role\": \"user\", \"content\": \"Hello\"}}]}}'{Colors.NC}") | |
| print() | |
| print(f"{Colors.BLUE}Tip:{Colors.NC} Get your API key from ~/cliproxyapi/config.yaml (api-keys section)") | |
| print() | |
| def print_usage(): | |
| """Print usage information""" | |
| print(f""" | |
| {Colors.CYAN}{Colors.BOLD}Antigravity Manager for CLIProxyAPI{Colors.NC} | |
| {Colors.YELLOW}Usage:{Colors.NC} | |
| python {sys.argv[0]} <command> | |
| {Colors.YELLOW}Commands:{Colors.NC} | |
| {Colors.GREEN}convert{Colors.NC} Convert antigravity accounts to CLIProxyAPI format | |
| {Colors.GREEN}refresh{Colors.NC} Refresh access tokens for existing credentials | |
| {Colors.GREEN}all{Colors.NC} Convert accounts then refresh tokens (default) | |
| {Colors.GREEN}help{Colors.NC} Show this help message | |
| {Colors.YELLOW}Examples:{Colors.NC} | |
| python {sys.argv[0]} # Run convert + refresh | |
| python {sys.argv[0]} convert # Only convert accounts | |
| python {sys.argv[0]} refresh # Only refresh tokens | |
| {Colors.YELLOW}File Locations:{Colors.NC} | |
| Source: {ANTIGRAVITY_FILE} | |
| Output: {CLIPROXYAPI_AUTH_DIR}/antigravity-*.json | |
| """) | |
| # ============================================================================ | |
| # Main | |
| # ============================================================================ | |
| def main(): | |
| command = sys.argv[1] if len(sys.argv) > 1 else "all" | |
| command = command.lower() | |
| if command in ("help", "-h", "--help"): | |
| print_usage() | |
| return 0 | |
| if command == "convert": | |
| result, _ = cmd_convert() | |
| print_next_steps() | |
| return result | |
| elif command == "refresh": | |
| return cmd_refresh() | |
| elif command == "all": | |
| # Convert first, then refresh | |
| convert_result, created_files = cmd_convert() | |
| if convert_result != 0: | |
| return convert_result | |
| if created_files: | |
| print() | |
| refresh_result = cmd_refresh() | |
| print_next_steps() | |
| return refresh_result | |
| else: | |
| log_warning("No files were created, skipping refresh") | |
| print_next_steps() | |
| return 0 | |
| else: | |
| log_error(f"Unknown command: {command}") | |
| print_usage() | |
| return 1 | |
| if __name__ == '__main__': | |
| sys.exit(main()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # CLIProxyAPI Linux Installer | |
| # Linux-specific script that installs, upgrades, and manages CLIProxyAPI | |
| # Downloads, installs, and upgrades CLIProxyAPI while preserving configuration | |
| set -euo pipefail | |
| # Configuration | |
| REPO_OWNER="router-for-me" | |
| REPO_NAME="CLIProxyAPI" | |
| REPO_NAME_PLUS="CLIProxyAPIPlus" | |
| INSTALL_DIR="$HOME/cliproxyapi" | |
| API_URL="https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest" | |
| API_URL_PLUS="https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME_PLUS}/releases/latest" | |
| API_URL_ALL="https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases" | |
| API_URL_ALL_PLUS="https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME_PLUS}/releases" | |
| SCRIPT_NAME="cliproxyapi-installer" | |
| VERSION_TYPE="standard" # can be "standard" or "plus" | |
| SPECIFIC_VERSION="" # specific version to install | |
| # Colors for output | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| CYAN='\033[0;36m' | |
| NC='\033[0m' # No Color | |
| # Logging functions | |
| log_info() { | |
| echo -e "${BLUE}[INFO]${NC} $1" | |
| } | |
| log_success() { | |
| echo -e "${GREEN}[SUCCESS]${NC} $1" | |
| } | |
| log_warning() { | |
| echo -e "${YELLOW}[WARNING]${NC} $1" | |
| } | |
| log_error() { | |
| echo -e "${RED}[ERROR]${NC} $1" | |
| } | |
| log_step() { | |
| echo -e "${CYAN}[STEP]${NC} $1" | |
| } | |
| # Display authentication information for first-time setup | |
| show_authentication_info() { | |
| echo | |
| echo -e "${YELLOW}🔐 IMPORTANT: Authentication Setup Required${NC}" | |
| echo | |
| echo -e "${BLUE}CLIProxyAPI supports authentication for multiple providers:${NC}" | |
| echo | |
| echo -e "${GREEN}📚 Full Documentation:${NC} https://github.com/router-for-me/CLIProxyAPI" | |
| echo | |
| echo -e "${YELLOW}Authentication Commands:${NC}" | |
| echo | |
| echo -e "${GREEN}Gemini (Google):${NC}" | |
| echo " ./cli-proxy-api --login" | |
| echo " ./cli-proxy-api --login --project_id <your_project_id>" | |
| echo " (OAuth callback on port 8085)" | |
| echo | |
| echo -e "${GREEN}OpenAI (Codex/GPT):${NC}" | |
| echo " ./cli-proxy-api --codex-login" | |
| echo " (OAuth callback on port 1455)" | |
| echo | |
| echo -e "${GREEN}Claude (Anthropic):${NC}" | |
| echo " ./cli-proxy-api --claude-login" | |
| echo " (OAuth callback on port 54545)" | |
| echo | |
| echo -e "${GREEN}Qwen (Qwen Chat):${NC}" | |
| echo " ./cli-proxy-api --qwen-login" | |
| echo " (Uses OAuth device flow)" | |
| echo | |
| echo -e "${GREEN}iFlow:${NC}" | |
| echo " ./cli-proxy-api --iflow-login" | |
| echo " (OAuth callback on port 11451)" | |
| echo | |
| echo -e "${YELLOW}💡 Tip: Add --no-browser to any login command to print URL instead" | |
| echo " of automatically opening a browser." | |
| echo | |
| } | |
| # Generate OpenAI-format API key | |
| generate_api_key() { | |
| # OpenAI API keys follow the format: sk-... (48 characters total) | |
| # Generate 48-character random string starting with "sk-" | |
| local prefix="sk-" | |
| local chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" | |
| local key="" | |
| # Generate 45 random characters (48 - 3 for "sk-") | |
| for i in {1..45}; do | |
| key="${key}${chars:$((RANDOM % ${#chars})):1}" | |
| done | |
| echo "${prefix}${key}" | |
| } | |
| # Manage documentation intelligently | |
| manage_documentation() { | |
| echo "Documentation Management" | |
| echo "========================" | |
| if ! is_installed; then | |
| log_error "CLIProxyAPI is not installed. Install it first to manage documentation." | |
| exit 1 | |
| fi | |
| local version_dir | |
| version_dir=$(get_current_version_dir) | |
| local readme_file="${version_dir}/README.md" | |
| local install_readme="${INSTALL_DIR}/README.md" | |
| echo "📚 Documentation Status:" | |
| echo | |
| # Check for README files | |
| if [[ -f "$readme_file" ]]; then | |
| echo "✅ Project README: Found ($readme_file)" | |
| else | |
| echo "❌ Project README: Missing" | |
| fi | |
| if [[ -f "$install_readme" ]]; then | |
| echo "✅ Installer README: Found ($install_readme)" | |
| else | |
| echo "❌ Installer README: Missing" | |
| fi | |
| # Check version consistency | |
| local current_version | |
| current_version=$(get_current_version) | |
| echo | |
| echo "🔄 Version Consistency Check:" | |
| if [[ -f "$readme_file" ]] && grep -q "$current_version" "$readme_file"; then | |
| echo "✅ Project README: Version $current_version referenced" | |
| else | |
| echo "⚠️ Project README: Version $current_version not found or outdated" | |
| fi | |
| if [[ -f "$install_readme" ]] && grep -q "$current_version" "$install_readme"; then | |
| echo "✅ Installer README: Version $current_version referenced" | |
| else | |
| echo "⚠️ Installer README: Version $current_version not found or outdated" | |
| fi | |
| # Check for common documentation issues | |
| echo | |
| echo "🔍 Documentation Quality Check:" | |
| local issues_found=0 | |
| # Check for broken links (basic check) | |
| if [[ -f "$install_readme" ]]; then | |
| if grep -q "https://" "$install_readme"; then | |
| echo "✅ External links: Present" | |
| else | |
| echo "ℹ️ External links: None found" | |
| fi | |
| fi | |
| # Check for TODO items or placeholders | |
| if [[ -f "$install_readme" ]] && grep -q -i "todo\|placeholder\|your-repo" "$install_readme"; then | |
| echo "⚠️ Placeholders found: Update repository URLs and TODO items" | |
| ((issues_found++)) | |
| fi | |
| # Check for consistent formatting | |
| if [[ -f "$install_readme" ]]; then | |
| local header_count=$(grep -c "^#" "$install_readme") | |
| echo "📊 Headers found: $header_count" | |
| if [[ $header_count -lt 5 ]]; then | |
| echo "⚠️ Documentation structure: May need more sections" | |
| ((issues_found++)) | |
| fi | |
| fi | |
| # Provide recommendations | |
| echo | |
| echo "💡 Recommendations:" | |
| if [[ ! -f "$install_readme" ]]; then | |
| echo "• Create installer README.md with usage instructions" | |
| fi | |
| if [[ $issues_found -gt 0 ]]; then | |
| echo "• Review and update $issues_found documentation issues" | |
| fi | |
| echo "• Ensure version numbers are current ($current_version)" | |
| echo "• Update repository URLs if changed" | |
| echo "• Add cross-references between documents" | |
| echo | |
| echo "📖 Quick Documentation Tasks:" | |
| echo "• View project docs: cat $readme_file" | |
| echo "• View installer docs: cat $install_readme" | |
| echo "• Edit installer docs: nano $install_readme" | |
| } | |
| # Check if API keys are configured | |
| check_api_keys() { | |
| local config_file="${INSTALL_DIR}/config.yaml" | |
| if [[ ! -f "$config_file" ]]; then | |
| return 1 | |
| fi | |
| # Check for default/placeholder API keys | |
| if grep -q '"your-api-key-1"' "$config_file" || grep -q '"your-api-key-2"' "$config_file"; then | |
| return 1 | |
| fi | |
| # Check if api-keys section exists and has non-empty values that look like real API keys | |
| if grep -A 10 "^api-keys:" "$config_file" | grep -v "^#" | grep -v "^api-keys:" | grep -q '"sk-[^"]*"'; then | |
| return 0 | |
| fi | |
| return 1 | |
| } | |
| # Show API key setup guidance | |
| show_api_key_setup() { | |
| echo | |
| echo -e "${YELLOW}🔑 IMPORTANT: API Keys Required Before First Run${NC}" | |
| echo | |
| echo -e "${BLUE}Before starting CLIProxyAPI, you need to configure API keys in config.yaml:${NC}" | |
| echo | |
| echo -e "${GREEN}1. Edit the configuration file:${NC}" | |
| echo -e " ${CYAN}nano ${INSTALL_DIR}/config.yaml${NC}" | |
| echo | |
| echo -e "${GREEN}2. Find the 'api-keys' section and replace placeholder keys:${NC}" | |
| echo | |
| echo -e " ${YELLOW}# Replace this:${NC}" | |
| echo -e " ${YELLOW}api-keys:${NC}" | |
| echo -e " ${YELLOW} - \"your-api-key-1\"${NC}" | |
| echo -e " ${YELLOW} - \"your-api-key-2\"${NC}" | |
| echo | |
| echo -e " ${GREEN}# With your actual API keys:${NC}" | |
| echo -e " ${GREEN}api-keys:${NC}" | |
| echo -e " ${GREEN} - \"sk-your-real-openai-key-here\"${NC}" | |
| echo -e " ${GREEN} - \"your-custom-secure-key-12345\"${NC}" | |
| echo | |
| echo -e "${GREEN}3. Save the file (Ctrl+X, then Y, then Enter in nano)${NC}" | |
| echo | |
| echo -e "${BLUE}💡 Tips:${NC}" | |
| echo -e " • Use strong, unique keys for security" | |
| echo -e " • You can add multiple keys for different users/services" | |
| echo -e " • Keys are used for authenticating requests to your proxy" | |
| echo -e " • These are NOT your provider API keys (those are set via login commands)" | |
| echo | |
| } | |
| # Show quick start guide | |
| show_quick_start() { | |
| local install_dir="$1" | |
| echo | |
| echo -e "${GREEN}🚀 Quick Start Guide:${NC}" | |
| echo -e "${BLUE}1. Navigate to CLIProxyAPI:${NC}" | |
| echo -e " ${CYAN}cd $install_dir${NC}" | |
| echo | |
| # Check if API keys are configured | |
| if ! check_api_keys; then | |
| show_api_key_setup | |
| echo -e "${BLUE}2. Set up authentication (choose one or more):${NC}" | |
| else | |
| echo -e "${BLUE}2. Set up authentication (choose one or more):${NC}" | |
| fi | |
| echo -e " ${CYAN}./cli-proxy-api --login${NC} # For Gemini" | |
| echo -e " ${CYAN}./cli-proxy-api --codex-login${NC} # For OpenAI" | |
| echo -e " ${CYAN}./cli-proxy-api --claude-login${NC} # For Claude" | |
| echo -e " ${CYAN}./cli-proxy-api --qwen-login${NC} # For Qwen" | |
| echo -e " ${CYAN}./cli-proxy-api --iflow-login${NC} # For iFlow" | |
| echo | |
| if check_api_keys; then | |
| echo -e "${BLUE}Your API keys:${NC}" | |
| grep -A 10 "^api-keys:" "${install_dir}/config.yaml" | grep -v "^#" | head -10 | |
| echo | |
| fi | |
| if ! check_api_keys; then | |
| echo -e "${BLUE}3. Configure API keys (REQUIRED):${NC}" | |
| echo -e " ${CYAN}nano config.yaml${NC} # Edit API keys" | |
| echo | |
| echo -e "${BLUE}4. Start the service:${NC}" | |
| echo -e " ${CYAN}./cli-proxy-api${NC}" | |
| echo | |
| echo -e "${BLUE}5. Or run as a systemd service:${NC}" | |
| echo -e " ${CYAN}systemctl --user enable cliproxyapi.service${NC}" | |
| echo -e " ${CYAN}systemctl --user start cliproxyapi.service${NC}" | |
| echo -e " ${CYAN}systemctl --user status cliproxyapi.service${NC}" | |
| echo | |
| echo -e "${BLUE}6. Read the full documentation:${NC}" | |
| echo -e " ${CYAN}https://github.com/router-for-me/CLIProxyAPI${NC}" | |
| else | |
| echo -e "${BLUE}3. Start the service:${NC}" | |
| echo -e " ${CYAN}./cli-proxy-api${NC}" | |
| echo | |
| echo -e "${BLUE}4. Or run as a systemd service:${NC}" | |
| echo -e " ${CYAN}systemctl --user enable cliproxyapi.service${NC}" | |
| echo -e " ${CYAN}systemctl --user start cliproxyapi.service${NC}" | |
| echo -e " ${CYAN}systemctl --user status cliproxyapi.service${NC}" | |
| echo | |
| echo -e "${BLUE}5. Read the full documentation:${NC}" | |
| echo -e " ${CYAN}https://github.com/router-for-me/CLIProxyAPI${NC}" | |
| fi | |
| echo | |
| } | |
| # Detect Linux architecture | |
| detect_linux_arch() { | |
| # Detect architecture | |
| case "$(uname -m)" in | |
| x86_64|amd64) | |
| echo "linux_amd64" | |
| ;; | |
| arm64|aarch64) | |
| echo "linux_arm64" | |
| ;; | |
| *) | |
| log_error "Unsupported architecture: $(uname -m). Only x86_64 and arm64 are supported on Linux." | |
| exit 1 | |
| ;; | |
| esac | |
| } | |
| # Check if required tools are available | |
| check_dependencies() { | |
| local missing_tools=() | |
| if ! command -v curl >/dev/null 2>&1 && ! command -v wget >/dev/null 2>&1; then | |
| missing_tools+=("curl or wget") | |
| fi | |
| if ! command -v tar >/dev/null 2>&1; then | |
| missing_tools+=("tar") | |
| fi | |
| if [[ ${#missing_tools[@]} -gt 0 ]]; then | |
| log_error "Missing required tools: ${missing_tools[*]}" | |
| log_info "Please install the missing tools and try again" | |
| log_info "On Ubuntu/Debian: sudo apt-get install curl wget tar" | |
| log_info "On CentOS/RHEL: sudo yum install curl wget tar" | |
| exit 1 | |
| fi | |
| } | |
| # Fetch all releases from GitHub API | |
| fetch_all_releases() { | |
| local version_type="${1:-standard}" | |
| local api_url="$API_URL_ALL" | |
| if [[ "$version_type" == "plus" ]]; then | |
| api_url="$API_URL_ALL_PLUS" | |
| fi | |
| local releases_info | |
| if command -v curl >/dev/null 2>&1; then | |
| releases_info=$(curl -s "$api_url") | |
| else | |
| releases_info=$(wget -qO- "$api_url") | |
| fi | |
| if [[ -z "$releases_info" ]]; then | |
| log_error "Failed to fetch releases information from GitHub API" | |
| exit 1 | |
| fi | |
| echo "$releases_info" | |
| } | |
| # Fetch latest release info from GitHub API | |
| fetch_release_info() { | |
| local version_type="${1:-standard}" | |
| local api_url="$API_URL" | |
| if [[ "$version_type" == "plus" ]]; then | |
| api_url="$API_URL_PLUS" | |
| log_info "Fetching latest CLIProxyAPI Plus release information..." | |
| else | |
| log_info "Fetching latest CLIProxyAPI release information..." | |
| fi | |
| local release_info | |
| if command -v curl >/dev/null 2>&1; then | |
| release_info=$(curl -s "$api_url") | |
| else | |
| release_info=$(wget -qO- "$api_url") | |
| fi | |
| if [[ -z "$release_info" ]]; then | |
| log_error "Failed to fetch release information from GitHub API" | |
| exit 1 | |
| fi | |
| echo "$release_info" | |
| } | |
| # Fetch specific version release info from GitHub API | |
| fetch_specific_version_info() { | |
| local version="$1" | |
| local version_type="${2:-standard}" | |
| local repo_name="$REPO_NAME" | |
| if [[ "$version_type" == "plus" ]]; then | |
| repo_name="$REPO_NAME_PLUS" | |
| fi | |
| local api_url="https://api.github.com/repos/${REPO_OWNER}/${repo_name}/releases/tags/v${version}" | |
| log_info "Fetching release information for version $version..." | |
| local release_info | |
| if command -v curl >/dev/null 2>&1; then | |
| release_info=$(curl -s "$api_url") | |
| else | |
| release_info=$(wget -qO- "$api_url") | |
| fi | |
| if [[ -z "$release_info" ]] || echo "$release_info" | grep -q '"message": "Not Found"'; then | |
| log_error "Version $version not found in GitHub releases" | |
| log_info "Use 'list-versions' command to see available versions" | |
| exit 1 | |
| fi | |
| echo "$release_info" | |
| } | |
| # List all available versions | |
| list_available_versions() { | |
| local version_type="${1:-standard}" | |
| check_dependencies | |
| log_info "Fetching available versions..." | |
| local releases_info | |
| releases_info=$(fetch_all_releases "$version_type") | |
| local versions | |
| versions=$(echo "$releases_info" | grep -o '"tag_name": *"[^"]*"' | cut -d'"' -f4 | sed 's/^v//') | |
| if [[ -z "$versions" ]]; then | |
| log_error "No versions found" | |
| exit 1 | |
| fi | |
| local current_version | |
| current_version=$(get_current_version) | |
| echo | |
| echo "Available CLIProxyAPI Versions ($version_type):" | |
| echo "=============================================" | |
| echo | |
| local count=0 | |
| while IFS= read -r version; do | |
| count=$((count + 1)) | |
| if [[ "$version" == "$current_version" ]]; then | |
| echo -e "${GREEN} $version ${CYAN}(installed)${NC}" | |
| else | |
| echo " $version" | |
| fi | |
| done <<< "$versions" | |
| echo | |
| echo "Total: $count versions available" | |
| echo | |
| echo "To install a specific version:" | |
| echo " $SCRIPT_NAME --version <version> install" | |
| echo | |
| echo "Example:" | |
| echo " $SCRIPT_NAME --version 1.0.0 install" | |
| echo | |
| } | |
| # Extract version and download URL from release info | |
| extract_release_info() { | |
| local release_info="$1" | |
| local os_arch="$2" | |
| local version_type="${3:-standard}" | |
| local version | |
| version=$(echo "$release_info" | grep -o '"tag_name": *"[^"]*"' | cut -d'"' -f4 | sed 's/^v//') | |
| if [[ -z "$version" ]]; then | |
| log_error "Failed to extract version from release info" | |
| exit 1 | |
| fi | |
| local expected_filename | |
| local download_url="" | |
| # Handle different naming conventions for standard vs plus versions | |
| if [[ "$version_type" == "plus" ]]; then | |
| expected_filename="CLIProxyAPIPlus_${version}_${os_arch}" | |
| else | |
| expected_filename="CLIProxyAPI_${version}_${os_arch}" | |
| fi | |
| # Handle different file extensions | |
| if [[ "$os_arch" == windows_* ]]; then | |
| expected_filename="${expected_filename}.zip" | |
| else | |
| expected_filename="${expected_filename}.tar.gz" | |
| fi | |
| download_url=$(echo "$release_info" | grep -o "\"browser_download_url\": *\"[^\"]*${expected_filename}[^\"]*\"" | cut -d'"' -f4) | |
| if [[ -z "$download_url" ]]; then | |
| log_error "Failed to find download URL for ${expected_filename}" | |
| exit 1 | |
| fi | |
| echo "${version}|${download_url}" | |
| } | |
| # Check if CLIProxyAPI is already installed | |
| is_installed() { | |
| [[ -f "${INSTALL_DIR}/version.txt" ]] | |
| } | |
| # Get installed version type (standard or plus) | |
| get_version_type() { | |
| if is_installed && [[ -f "${INSTALL_DIR}/version_type.txt" ]]; then | |
| cat "${INSTALL_DIR}/version_type.txt" 2>/dev/null || echo "standard" | |
| else | |
| echo "standard" | |
| fi | |
| } | |
| # Get currently installed version | |
| get_current_version() { | |
| if is_installed; then | |
| cat "${INSTALL_DIR}/version.txt" 2>/dev/null || echo "unknown" | |
| else | |
| echo "none" | |
| fi | |
| } | |
| # Get current version directory | |
| get_current_version_dir() { | |
| local current_version | |
| current_version=$(get_current_version) | |
| if [[ "$current_version" != "none" ]]; then | |
| echo "${INSTALL_DIR}/${current_version}" | |
| else | |
| echo "" | |
| fi | |
| } | |
| # Backup existing configuration | |
| backup_config() { | |
| local config="${INSTALL_DIR}/config.yaml" | |
| if [[ -f "$config" ]]; then | |
| local backup_dir="${INSTALL_DIR}/config_backup" | |
| mkdir -p "$backup_dir" | |
| local timestamp | |
| timestamp=$(date +"%Y%m%d_%H%M%S") | |
| local backup_file="${backup_dir}/config_${timestamp}.yaml" | |
| cp "$config" "$backup_file" | |
| log_info "Configuration backed up to: $backup_file" | |
| echo "$backup_file" | |
| else | |
| echo "" | |
| fi | |
| } | |
| # Restore configuration to new version | |
| restore_config() { | |
| local new_version_dir="$1" | |
| local backup_file="$2" | |
| if [[ -n "$backup_file" && -f "$backup_file" ]]; then | |
| cp "$backup_file" "${new_version_dir}/config.yaml" | |
| log_success "Configuration restored from backup" | |
| fi | |
| } | |
| # Check if systemd service is running | |
| is_service_running() { | |
| systemctl --user is-active --quiet cliproxyapi.service 2>/dev/null | |
| } | |
| # Check if any CLIProxyAPI processes are running | |
| is_cliproxyapi_running() { | |
| pgrep -f "cli-proxy-api" >/dev/null 2>&1 | |
| } | |
| # Stop any running CLIProxyAPI processes | |
| stop_cliproxyapi_processes() { | |
| local pids | |
| pids=$(pgrep -f "cli-proxy-api" 2>/dev/null || true) | |
| if [[ -n "$pids" ]]; then | |
| log_info "Stopping running CLIProxyAPI processes..." | |
| echo "$pids" | while read -r pid; do | |
| if [[ -n "$pid" ]]; then | |
| kill "$pid" 2>/dev/null || true | |
| log_info "Sent TERM signal to process $pid" | |
| fi | |
| done | |
| # Wait a moment for graceful shutdown | |
| sleep 2 | |
| # Check if any processes are still running and force kill if needed | |
| local remaining_pids | |
| remaining_pids=$(pgrep -f "cli-proxy-api" 2>/dev/null || true) | |
| if [[ -n "$remaining_pids" ]]; then | |
| log_warning "Some processes didn't stop gracefully, force killing..." | |
| echo "$remaining_pids" | while read -r pid; do | |
| if [[ -n "$pid" ]]; then | |
| kill -9 "$pid" 2>/dev/null || true | |
| log_info "Force killed process $pid" | |
| fi | |
| done | |
| sleep 1 | |
| fi | |
| log_success "All CLIProxyAPI processes stopped" | |
| else | |
| log_info "No CLIProxyAPI processes are running" | |
| fi | |
| } | |
| # Stop systemd service if running | |
| stop_service() { | |
| if is_service_running; then | |
| log_info "Stopping CLIProxyAPI service..." | |
| systemctl --user stop cliproxyapi.service | |
| log_success "Service stopped" | |
| else | |
| log_info "Service is not running" | |
| fi | |
| } | |
| # Start systemd service | |
| start_service() { | |
| log_info "Starting CLIProxyAPI service..." | |
| systemctl --user start cliproxyapi.service | |
| # Wait a moment and check if it started successfully | |
| sleep 2 | |
| if is_service_running; then | |
| log_success "Service started successfully" | |
| else | |
| log_warning "Service may not have started properly. Check with: systemctl --user status cliproxyapi.service" | |
| fi | |
| } | |
| # Restart systemd service | |
| restart_service() { | |
| log_info "Restarting CLIProxyAPI service..." | |
| systemctl --user restart cliproxyapi.service | |
| # Wait a moment and check if it started successfully | |
| sleep 2 | |
| if is_service_running; then | |
| log_success "Service restarted successfully" | |
| else | |
| log_warning "Service may not have started properly. Check with: systemctl --user status cliproxyapi.service" | |
| fi | |
| } | |
| # Create systemd service file | |
| create_systemd_service() { | |
| local install_dir="$1" | |
| local service_file="${install_dir}/cliproxyapi.service" | |
| local systemd_dir="$HOME/.config/systemd/user" | |
| local systemd_service_file="${systemd_dir}/cliproxyapi.service" | |
| log_info "Creating systemd service file..." | |
| # Create systemd user directory | |
| mkdir -p "$systemd_dir" | |
| # Create service file content with basic working configuration | |
| cat > "$service_file" << EOF | |
| [Unit] | |
| Description=CLIProxyAPI Service | |
| After=network.target | |
| [Service] | |
| Type=simple | |
| WorkingDirectory=$install_dir | |
| ExecStart=$install_dir/cli-proxy-api | |
| Restart=always | |
| RestartSec=10 | |
| Environment=HOME=$HOME | |
| [Install] | |
| WantedBy=default.target | |
| EOF | |
| # Copy to systemd user directory | |
| cp "$service_file" "$systemd_service_file" | |
| # Reload systemd daemon | |
| systemctl --user daemon-reload || log_warning "Could not reload systemd daemon (this is normal on first run)" | |
| log_success "Systemd service file created: $service_file" | |
| log_success "Systemd service installed: $systemd_service_file" | |
| log_info "To enable and start the service:" | |
| log_info " systemctl --user enable cliproxyapi.service" | |
| log_info " systemctl --user start cliproxyapi.service" | |
| log_info " systemctl --user status cliproxyapi.service" | |
| } | |
| # Copy example config if no existing config and setup main directory structure | |
| setup_config() { | |
| local version_dir="$1" | |
| local backup_file="$2" | |
| local version_type="${3:-standard}" | |
| log_info "Setting up configuration..." | |
| local config="${INSTALL_DIR}/config.yaml" | |
| local example_config="${version_dir}/config.example.yaml" | |
| local executable | |
| # Determine executable name based on version type | |
| if [[ "$version_type" == "plus" ]]; then | |
| executable="${version_dir}/cli-proxy-api-plus" | |
| else | |
| executable="${version_dir}/cli-proxy-api" | |
| fi | |
| # Copy executable to main directory with standard name for compatibility | |
| if [[ -f "$executable" ]]; then | |
| cp "$executable" "${INSTALL_DIR}/cli-proxy-api" | |
| log_success "Copied executable to ${INSTALL_DIR}/cli-proxy-api" | |
| fi | |
| # PRIORITY 1: If we have a backup from this upgrade, restore it | |
| if [[ -n "$backup_file" && -f "$backup_file" ]]; then | |
| cp "$backup_file" "$config" | |
| log_success "Restored configuration from backup" | |
| return | |
| fi | |
| # PRIORITY 2: Check for existing config in main directory (NEVER overwrite) | |
| if [[ -f "$config" ]]; then | |
| log_success "Preserved existing user configuration (config.yaml)" | |
| log_info "User modifications are protected during upgrades" | |
| return | |
| fi | |
| # PRIORITY 3: Check for existing config in previous version directory | |
| local current_version_dir | |
| current_version_dir=$(get_current_version_dir) | |
| if [[ -n "$current_version_dir" && -f "${current_version_dir}/config.yaml" ]]; then | |
| cp "${current_version_dir}/config.yaml" "$config" | |
| log_success "Preserved existing configuration from previous version" | |
| return | |
| fi | |
| # PRIORITY 4: Only create from example if NO existing config found | |
| if [[ -f "$example_config" ]]; then | |
| cp "$example_config" "$config" | |
| # Generate and replace API key | |
| local generated_key1 | |
| local generated_key2 | |
| generated_key1=$(generate_api_key) | |
| generated_key2=$(generate_api_key) | |
| # Replace placeholder API keys with generated keys | |
| sed -i "s/\"your-api-key-1\"/\"$generated_key1\"/g" "$config" | |
| sed -i "s/\"your-api-key-2\"/\"$generated_key2\"/g" "$config" | |
| log_success "Created config.yaml from example with generated API keys" | |
| log_info "Generated API keys: $generated_key1, $generated_key2" | |
| log_info "You can find your API keys in: $config" | |
| else | |
| log_warning "config.example.yaml not found, you may need to create config.yaml manually" | |
| fi | |
| } | |
| # Download file | |
| download_file() { | |
| local url="$1" | |
| local output="$2" | |
| log_info "Downloading $(basename "$url")..." | |
| if command -v curl >/dev/null 2>&1; then | |
| curl -L -o "$output" "$url" | |
| else | |
| wget -O "$output" "$url" | |
| fi | |
| if [[ ! -f "$output" ]]; then | |
| log_error "Failed to download file" | |
| exit 1 | |
| fi | |
| log_success "Download completed" | |
| } | |
| # Extract tar.gz archive | |
| extract_archive() { | |
| local archive="$1" | |
| local dest_dir="$2" | |
| log_info "Extracting archive to $dest_dir..." | |
| mkdir -p "$dest_dir" | |
| tar -xzf "$archive" -C "$dest_dir" | |
| log_success "Extraction completed" | |
| } | |
| # Write version file | |
| write_version_file() { | |
| local install_dir="$1" | |
| local version="$2" | |
| local version_type="${3:-standard}" | |
| echo "$version" > "${install_dir}/version.txt" | |
| echo "$version_type" > "${install_dir}/version_type.txt" | |
| log_success "Version $version ($version_type) written to version.txt" | |
| } | |
| # Clean up old versions (keep last 2 versions) | |
| cleanup_old_versions() { | |
| local current_version="$1" | |
| if [[ ! -d "$INSTALL_DIR" ]]; then | |
| return | |
| fi | |
| log_info "Cleaning up old versions..." | |
| # Get all version directories, sort them, and remove all but the latest 2 | |
| local old_versions | |
| old_versions=$(find "$INSTALL_DIR" -maxdepth 1 -type d -name "*.*.*" -printf "%f\n" | sort -V | head -n -2) | |
| if [[ -n "$old_versions" ]]; then | |
| echo "$old_versions" | while read -r version; do | |
| if [[ "$version" != "$current_version" && -n "$version" ]]; then | |
| rm -rf "${INSTALL_DIR}/${version}" | |
| log_info "Removed old version: $version" | |
| fi | |
| done | |
| fi | |
| } | |
| # Make binary executable | |
| make_executable() { | |
| local version_dir="$1" | |
| local version_type="${2:-standard}" | |
| local binary | |
| if [[ "$version_type" == "plus" ]]; then | |
| binary=$(find "$version_dir" -name "CLIProxyAPIPlus" -o -name "cli-proxy-api-plus" -type f | head -1) | |
| else | |
| binary=$(find "$version_dir" -name "CLIProxyAPI" -o -name "cli-proxy-api" -type f | head -1) | |
| fi | |
| if [[ -n "$binary" ]]; then | |
| chmod +x "$binary" | |
| log_success "Made CLIProxyAPI binary executable" | |
| fi | |
| } | |
| # Main installation function | |
| install_cliproxyapi() { | |
| local version_type="${VERSION_TYPE:-standard}" | |
| local specific_version="${SPECIFIC_VERSION}" | |
| local current_version | |
| current_version=$(get_current_version) | |
| local current_version_type | |
| current_version_type=$(get_version_type) | |
| local is_upgrade=false | |
| local service_was_running=false | |
| if [[ "$current_version" != "none" ]]; then | |
| log_info "Current CLIProxyAPI version: $current_version ($current_version_type)" | |
| is_upgrade=true | |
| if is_service_running; then | |
| service_was_running=true | |
| log_info "Service is currently running and will be restarted after upgrade" | |
| fi | |
| else | |
| log_info "CLIProxyAPI not installed, performing fresh installation" | |
| fi | |
| check_dependencies | |
| local os_arch | |
| os_arch=$(detect_linux_arch) | |
| log_step "Detected platform: $os_arch" | |
| local release_info | |
| if [[ -n "$specific_version" ]]; then | |
| release_info=$(fetch_specific_version_info "$specific_version" "$version_type") | |
| else | |
| release_info=$(fetch_release_info "$version_type") | |
| fi | |
| local release_data | |
| release_data=$(extract_release_info "$release_info" "$os_arch" "$version_type") | |
| local version=$(echo "$release_data" | cut -d'|' -f1) | |
| local download_url=$(echo "$release_data" | cut -d'|' -f2) | |
| log_step "Target version: $version ($version_type)" | |
| if [[ "$is_upgrade" == true && "$current_version" == "$version" && "$current_version_type" == "$version_type" ]]; then | |
| log_success "CLIProxyAPI is already at version $version ($version_type)" | |
| return | |
| fi | |
| if [[ "$is_upgrade" == true ]]; then | |
| if is_service_running; then | |
| stop_service | |
| fi | |
| if is_cliproxyapi_running; then | |
| stop_cliproxyapi_processes | |
| fi | |
| fi | |
| local backup_file="" | |
| if [[ "$is_upgrade" == true ]]; then | |
| backup_file=$(backup_config) | |
| fi | |
| local version_dir="${INSTALL_DIR}/${version}" | |
| mkdir -p "$INSTALL_DIR" | |
| local temp_file | |
| temp_file=$(mktemp) | |
| download_file "$download_url" "$temp_file" | |
| extract_archive "$temp_file" "$version_dir" | |
| rm -f "$temp_file" | |
| make_executable "$version_dir" "$version_type" | |
| setup_config "$version_dir" "$backup_file" "$version_type" | |
| chmod +x "${INSTALL_DIR}/cli-proxy-api" | |
| create_systemd_service "$INSTALL_DIR" | |
| write_version_file "$INSTALL_DIR" "$version" "$version_type" | |
| cleanup_old_versions "$version" | |
| if [[ "$is_upgrade" == true && "$service_was_running" == true ]]; then | |
| restart_service | |
| fi | |
| if [[ "$is_upgrade" == true ]]; then | |
| log_success "CLIProxyAPI upgraded from $current_version to $version!" | |
| log_info "Installation directory: $INSTALL_DIR" | |
| if [[ "$service_was_running" == true ]]; then | |
| log_info "Service has been restarted automatically" | |
| elif is_service_running; then | |
| log_info "Service is running" | |
| else | |
| log_info "To start the service: systemctl --user start cliproxyapi.service" | |
| fi | |
| if [[ ! -f "${INSTALL_DIR}/config.yaml" ]]; then | |
| show_authentication_info | |
| show_quick_start "$INSTALL_DIR" | |
| else | |
| log_info "Your existing configuration has been preserved" | |
| log_info "To use CLIProxyAPI: cd $INSTALL_DIR && ./cli-proxy-api --help" | |
| fi | |
| else | |
| log_success "CLIProxyAPI $version installed successfully!" | |
| log_info "Installation directory: $INSTALL_DIR" | |
| show_authentication_info | |
| show_quick_start "$INSTALL_DIR" | |
| fi | |
| } | |
| # Show current installation status | |
| show_status() { | |
| local current_version | |
| current_version=$(get_current_version) | |
| local current_version_type | |
| current_version_type=$(get_version_type) | |
| echo "CLIProxyAPI Installation Status" | |
| echo "==============================" | |
| echo "Install Directory: $INSTALL_DIR" | |
| echo "Current Version: $current_version ($current_version_type)" | |
| if [[ "$current_version" != "none" ]]; then | |
| local current_version_dir | |
| current_version_dir=$(get_current_version_dir) | |
| echo "Version Directory: $current_version_dir" | |
| if [[ -f "${INSTALL_DIR}/config.yaml" ]]; then | |
| echo "Configuration: Present (at ${INSTALL_DIR}/config.yaml)" | |
| else | |
| echo "Configuration: Missing" | |
| fi | |
| if [[ -f "${INSTALL_DIR}/cli-proxy-api" ]]; then | |
| echo "Executable: Present (at ${INSTALL_DIR}/cli-proxy-api)" | |
| else | |
| echo "Executable: Missing" | |
| fi | |
| if [[ -f "${INSTALL_DIR}/cliproxyapi.service" ]]; then | |
| echo "Systemd Service: Available" | |
| echo " To enable: systemctl --user enable cliproxyapi.service" | |
| echo " To start: systemctl --user start cliproxyapi.service" | |
| else | |
| echo "Systemd Service: Missing" | |
| fi | |
| # Check API keys status | |
| if check_api_keys; then | |
| echo "API Keys: Configured" | |
| else | |
| echo -e "API Keys: ${YELLOW}NOT CONFIGURED${NC} - Edit config.yaml to add API keys" | |
| fi | |
| # Show available versions | |
| if [[ -d "$INSTALL_DIR" ]]; then | |
| echo "Installed Versions:" | |
| find "$INSTALL_DIR" -maxdepth 1 -type d -name "*.*.*" -printf " %f\n" | sort -V | |
| fi | |
| else | |
| echo "Status: Not installed" | |
| fi | |
| } | |
| # Uninstall function | |
| uninstall_cliproxyapi() { | |
| if [[ ! -d "$INSTALL_DIR" ]]; then | |
| log_warning "CLIProxyAPI installation directory not found: $INSTALL_DIR" | |
| exit 0 | |
| fi | |
| log_info "CLIProxyAPI installation found at: $INSTALL_DIR" | |
| # Show what will be removed | |
| echo | |
| log_info "The following will be removed:" | |
| find "$INSTALL_DIR" -type f -exec echo " {}" \; | |
| echo | |
| # Ask for confirmation | |
| read -p "Are you sure you want to remove CLIProxyAPI? (y/N): " -n 1 -r | |
| echo | |
| if [[ ! $REPLY =~ ^[Yy]$ ]]; then | |
| log_info "Uninstallation cancelled" | |
| exit 0 | |
| fi | |
| # Remove installation directory | |
| log_info "Removing CLIProxyAPI installation..." | |
| rm -rf "$INSTALL_DIR" | |
| log_success "CLIProxyAPI has been uninstalled successfully" | |
| } | |
| # Cleanup function on exit | |
| cleanup() { | |
| local temp_files | |
| temp_files=$(find /tmp -name "tmp.*" -user "$(whoami)" 2>/dev/null || true) | |
| if [[ -n "$temp_files" ]]; then | |
| echo "$temp_files" | xargs rm -f 2>/dev/null || true | |
| fi | |
| } | |
| # Set up cleanup trap | |
| trap cleanup EXIT | |
| # Main script logic | |
| main() { | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| --plus) | |
| VERSION_TYPE="plus" | |
| shift | |
| ;; | |
| --standard) | |
| VERSION_TYPE="standard" | |
| shift | |
| ;; | |
| --version) | |
| if [[ -z "$2" || "$2" == -* ]]; then | |
| log_error "Option --version requires a version number" | |
| echo "Example: $SCRIPT_NAME --version 1.0.0 install" | |
| exit 1 | |
| fi | |
| SPECIFIC_VERSION="$2" | |
| shift 2 | |
| ;; | |
| *) | |
| break | |
| ;; | |
| esac | |
| done | |
| case "${1:-install}" in | |
| "install"|"upgrade") | |
| install_cliproxyapi | |
| ;; | |
| "list-versions"|"versions") | |
| list_available_versions "$VERSION_TYPE" | |
| ;; | |
| "status") | |
| show_status | |
| ;; | |
| "auth") | |
| show_authentication_info | |
| if is_installed; then | |
| local current_version_dir | |
| current_version_dir=$(get_current_version_dir) | |
| if [[ -n "$current_version_dir" ]]; then | |
| show_quick_start "$current_version_dir" | |
| fi | |
| fi | |
| ;; | |
| "check-config") | |
| if is_installed; then | |
| echo "Configuration Check" | |
| echo "==================" | |
| echo "Config file: ${INSTALL_DIR}/config.yaml" | |
| if check_api_keys; then | |
| echo -e "✅ API Keys: ${GREEN}Configured${NC}" | |
| echo -e "✅ Status: ${GREEN}Ready to run${NC}" | |
| echo | |
| echo -e "${BLUE}Current API keys in config.yaml:${NC}" | |
| grep -A 10 "^api-keys:" "${INSTALL_DIR}/config.yaml" | grep -v "^#" | head -10 | |
| echo | |
| echo -e "${BLUE}You can now start CLIProxyAPI:${NC}" | |
| echo -e " ${CYAN}cd ${INSTALL_DIR}${NC}" | |
| echo -e " ${CYAN}./cli-proxy-api${NC}" | |
| echo | |
| echo -e "${BLUE}Or run as a service:${NC}" | |
| echo -e " ${CYAN}systemctl --user start cliproxyapi.service${NC}" | |
| else | |
| echo -e "❌ API Keys: ${RED}NOT CONFIGURED${NC}" | |
| echo -e "❌ Status: ${RED}Not ready to run${NC}" | |
| echo | |
| show_api_key_setup | |
| fi | |
| else | |
| log_error "CLIProxyAPI is not installed" | |
| echo "Install it first with: $SCRIPT_NAME install" | |
| fi | |
| ;; | |
| "generate-key") | |
| local new_key | |
| new_key=$(generate_api_key) | |
| echo "Generated OpenAI-format API Key:" | |
| echo "================================" | |
| echo -e "${GREEN}$new_key${NC}" | |
| echo | |
| echo -e "${BLUE}To use this key, add it to your config.yaml:${NC}" | |
| echo -e "${CYAN}api-keys:${NC}" | |
| echo -e "${CYAN} - \"$new_key\"${NC}" | |
| echo | |
| echo -e "${YELLOW}Note: This generates a new key but doesn't modify your config file.${NC}" | |
| echo -e "${YELLOW}Edit ${INSTALL_DIR}/config.yaml manually to add this key.${NC}" | |
| ;; | |
| "manage-docs") | |
| manage_documentation | |
| ;; | |
| "uninstall") | |
| uninstall_cliproxyapi | |
| ;; | |
| "-h"|"--help") | |
| cat <<EOF | |
| CLIProxyAPI Linux Installer | |
| Usage: $SCRIPT_NAME [OPTIONS] [COMMAND] | |
| Options: | |
| --standard Install standard version (default) | |
| --plus Install Plus version from CLIProxyAPIPlus repository | |
| --version VERSION Install specific version number (e.g., --version 1.0.0) | |
| Commands: | |
| install, upgrade Install or upgrade CLIProxyAPI (default) | |
| list-versions List all available versions from GitHub | |
| versions Alias for list-versions | |
| status Show current installation status | |
| auth Show authentication setup information | |
| check-config Check configuration and API keys | |
| generate-key Generate new API key and show it | |
| manage-docs Manage documentation (update, organize, check consistency) | |
| uninstall Remove CLIProxyAPI completely | |
| -h, --help Show this help message | |
| Description: | |
| This Linux-specific script installs, upgrades, or removes CLIProxyAPI. | |
| It can be run from anywhere and automatically detects your Linux architecture. | |
| During upgrades, your config.yaml file is preserved automatically. | |
| Features: | |
| - Automatic Linux architecture detection (amd64/arm64) | |
| - Downloads latest or specific version from GitHub releases | |
| - Install any older version available | |
| - Preserves configuration during upgrades | |
| - Automatic API key generation | |
| - Systemd service file creation | |
| - Cleans up old versions (keeps latest 2) | |
| - Support for both standard and Plus versions | |
| Installation Directory: ~/cliproxyapi/ | |
| Supported Platforms: | |
| - Linux: amd64, arm64 | |
| Repositories: | |
| - Standard: https://github.com/router-for-me/CLIProxyAPI | |
| - Plus: https://github.com/router-for-me/CLIProxyAPIPlus | |
| Examples: | |
| $SCRIPT_NAME # Install or upgrade (standard, latest) | |
| $SCRIPT_NAME --plus install # Install Plus version (latest) | |
| $SCRIPT_NAME --version 1.0.0 install # Install specific version 1.0.0 | |
| $SCRIPT_NAME --plus --version 2.3.1 # Install Plus version 2.3.1 | |
| $SCRIPT_NAME list-versions # Show all available versions | |
| $SCRIPT_NAME --plus versions # Show Plus versions | |
| $SCRIPT_NAME status # Show current status | |
| $SCRIPT_NAME auth # Show authentication info | |
| $SCRIPT_NAME check-config # Check configuration | |
| $SCRIPT_NAME generate-key # Generate new API key | |
| $SCRIPT_NAME --plus upgrade # Upgrade to latest Plus version | |
| $SCRIPT_NAME uninstall # Remove completely | |
| EOF | |
| ;; | |
| *) | |
| log_error "Unknown command: $1" | |
| echo "Use '$SCRIPT_NAME --help' for usage information" | |
| exit 1 | |
| ;; | |
| esac | |
| } | |
| # Run main function with all arguments | |
| main "$@" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "model": "cliproxy/claude-sonnet-4-5-thinking", | |
| "permission": { | |
| "question": "allow" | |
| }, | |
| "plugin": [ | |
| "oh-my-opencode@3.0.0-beta.11" | |
| ], | |
| "provider": { | |
| "cliproxy": { | |
| "name": "Proxy API", | |
| "npm": "@ai-sdk/anthropic", | |
| "options": { | |
| "apiKey": "its_norman_here", | |
| "baseURL": "http://localhost:8317/v1", | |
| "timeout": 600000 | |
| }, | |
| "models": { | |
| "claude-opus-4-5-thinking": { | |
| "name": "Claude Opus 4.5 Thinking", | |
| "limit": { | |
| "context": 200000, | |
| "output": 64000 | |
| }, | |
| "reasoning": true, | |
| "options": { | |
| "thinking": { | |
| "type": "enabled", | |
| "budgetTokens": 32000 | |
| } | |
| } | |
| }, | |
| "claude-sonnet-4-5": { | |
| "name": "Claude Sonnet 4.5", | |
| "limit": { | |
| "context": 200000, | |
| "output": 64000 | |
| } | |
| }, | |
| "claude-sonnet-4-5-thinking": { | |
| "name": "Claude Sonnet 4.5 Thinking", | |
| "limit": { | |
| "context": 200000, | |
| "output": 64000 | |
| }, | |
| "reasoning": true, | |
| "options": { | |
| "thinking": { | |
| "type": "enabled", | |
| "budgetTokens": 32000 | |
| } | |
| } | |
| } | |
| } | |
| }, | |
| "cliproxygoogle": { | |
| "name": "Proxy API (Google)", | |
| "npm": "@ai-sdk/google", | |
| "options": { | |
| "apiKey": "its_norman_here", | |
| "baseURL": "http://localhost:8317/v1beta", | |
| "timeout": 600000 | |
| }, | |
| "models": { | |
| "gemini-3-pro-preview": { | |
| "name": "Gemini 3 Pro Preview", | |
| "reasoning": true, | |
| "limit": { | |
| "context": 1048576, | |
| "output": 65536 | |
| } | |
| }, | |
| "gemini-3-pro-image-preview": { | |
| "name": "Gemini 3 Pro Image Preview", | |
| "limit": { | |
| "context": 1048576, | |
| "output": 65536 | |
| } | |
| }, | |
| "gemini-3-flash-preview": { | |
| "name": "Gemini 3 Flash Preview", | |
| "limit": { | |
| "context": 1048576, | |
| "output": 65536 | |
| } | |
| }, | |
| "gemini-2.5-flash": { | |
| "name": "Gemini 2.5 Flash", | |
| "limit": { | |
| "context": 1048576, | |
| "output": 65536 | |
| } | |
| }, | |
| "gemini-2.5-flash-lite": { | |
| "name": "Gemini 2.5 Flash Lite", | |
| "limit": { | |
| "context": 1048576, | |
| "output": 65536 | |
| } | |
| }, | |
| "gemini-2.5-computer-use-preview-10-2025": { | |
| "name": "Gemini 2.5 Computer Use Preview", | |
| "limit": { | |
| "context": 1048576, | |
| "output": 65536 | |
| } | |
| } | |
| } | |
| } | |
| }, | |
| "$schema": "https://opencode.ai/config.json" | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment