diff --git a/scripts/run/cursor_linux_id_modifier.sh b/scripts/run/cursor_linux_id_modifier.sh index 2458d76..72d3296 100644 --- a/scripts/run/cursor_linux_id_modifier.sh +++ b/scripts/run/cursor_linux_id_modifier.sh @@ -1,474 +1,210 @@ #!/bin/bash # 设置错误处理 -set -e +set -eo pipefail # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' -NC='\033[0m' # No Color +NC='\033[0m' # 日志函数 -log_info() { - echo -e "${GREEN}[INFO]${NC} $1" -} - -log_warn() { - echo -e "${YELLOW}[WARN]${NC} $1" -} - -log_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -log_debug() { - echo -e "${BLUE}[DEBUG]${NC} $1" -} - -# 获取当前用户 -get_current_user() { - if [ "$EUID" -eq 0 ]; then - echo "$SUDO_USER" - else - echo "$USER" - fi -} - -CURRENT_USER=$(get_current_user) -if [ -z "$CURRENT_USER" ]; then - log_error "无法获取用户名" - exit 1 -fi - -# 定义配置文件路径 -if [ "$EUID" -eq 0 ] && [ -n "$SUDO_USER" ]; then - USER_HOME=$(eval echo ~$SUDO_USER) -else - USER_HOME="$HOME" -fi -STORAGE_FILE="$USER_HOME/.config/Cursor/User/globalStorage/storage.json" -BACKUP_DIR="$USER_HOME/.config/Cursor/User/globalStorage/backups" - -# 检查权限 +log() { + local level=$1; shift + local color + case "$level" in + "INFO") color="$GREEN" ;; + "WARN") color="$YELLOW" ;; + "ERROR") color="$RED" ;; + "DEBUG") color="$BLUE" ;; + *) color="$NC" ;; + esac + echo -e "${color}[$level]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $*" +} + +# 检查访问权限 check_permissions() { - if [ "$EUID" -ne 0 ]; then - log_error "请使用 sudo 运行此脚本" - echo "示例: sudo $0" - exit 1 - fi -} - -# 检查并关闭 Cursor 进程 -check_and_kill_cursor() { - log_info "检查 Cursor 进程..." - - local attempt=1 - local max_attempts=5 - - # 函数:获取进程详细信息 - get_process_details() { - local process_name="$1" - log_debug "正在获取 $process_name 进程详细信息:" - ps aux | grep -i "$process_name" | grep -v grep - } - - while [ $attempt -le $max_attempts ]; do - CURSOR_PIDS=$(pgrep -i "cursor" || true) - - if [ -z "$CURSOR_PIDS" ]; then - log_info "未发现运行中的 Cursor 进程" - return 0 - fi - - log_warn "发现 Cursor 进程正在运行" - get_process_details "cursor" - - log_warn "尝试关闭 Cursor 进程..." - - if [ $attempt -eq $max_attempts ]; then - log_warn "尝试强制终止进程..." - kill -9 $CURSOR_PIDS 2>/dev/null || true - else - kill $CURSOR_PIDS 2>/dev/null || true - fi - - sleep 1 - - if ! pgrep -i "cursor" > /dev/null; then - log_info "Cursor 进程已成功关闭" - return 0 - fi - - log_warn "等待进程关闭,尝试 $attempt/$max_attempts..." - ((attempt++)) - done - - log_error "在 $max_attempts 次尝试后仍无法关闭 Cursor 进程" - get_process_details "cursor" - log_error "请手动关闭进程后重试" + if [ "$(id -u)" -ne 0 ]; then + log "ERROR" "所需许可证 root. 用 sudo 运行:" + echo " sudo $0" exit 1 -} - -# 备份系统 ID -backup_system_id() { - log_info "正在备份系统 ID..." - local system_id_file="$BACKUP_DIR/system_id.backup_$(date +%Y%m%d_%H%M%S)" - - # 创建备份目录 - mkdir -p "$BACKUP_DIR" - - { - echo "# Original System ID Backup - $(date)" > "$system_id_file" - echo "## Machine ID:" >> "$system_id_file" - cat /etc/machine-id >> "$system_id_file" - echo -e "\n## DMI System UUID:" >> "$system_id_file" - dmidecode -s system-uuid >> "$system_id_file" 2>/dev/null || echo "N/A" - - chmod 444 "$system_id_file" - chown "$CURRENT_USER" "$system_id_file" - log_info "系统 ID 已备份到: $system_id_file" - } || { - log_error "备份系统 ID 失败" - return 1 - } -} - -# 备份配置文件 -backup_config() { - if [ ! -f "$STORAGE_FILE" ]; then - log_warn "配置文件不存在,跳过备份" - return 0 + fi +} + +# 查找 Cursor 安装文件夹 +find_cursor_dir() { + local possible_dirs=( + "/opt/Cursor" + "/opt/cursor-bin" + "/usr/lib/cursor" + "$HOME/.cursor" + ) + + for dir in "${possible_dirs[@]}"; do + if [ -d "$dir" ]; then + echo "$dir" + return 0 fi - - mkdir -p "$BACKUP_DIR" - local backup_file="$BACKUP_DIR/storage.json.backup_$(date +%Y%m%d_%H%M%S)" - - if cp "$STORAGE_FILE" "$backup_file"; then - chmod 644 "$backup_file" - chown "$CURRENT_USER" "$backup_file" - log_info "配置已备份到: $backup_file" - else - log_error "备份失败" - exit 1 - fi -} + done -# 生成随机 ID -generate_random_id() { - # 生成32字节(64个十六进制字符)的随机数,并确保一行输出 - head -c 32 /dev/urandom | xxd -p -c 32 + log "ERROR" "无法找到文件夹 Cursor" + exit 1 } -# 生成随机 UUID -generate_uuid() { - uuidgen | tr '[:upper:]' '[:lower:]' +# 获取准确的流程清单 +get_cursor_pids() { + local cursor_dir=$(find_cursor_dir) + ps aux | grep -iE "$cursor_dir/[cC]ursor" | grep -v -E 'grep|--type=renderer' | awk '{print $2}' } -# 修改现有文件 -modify_or_add_config() { - local key="$1" - local value="$2" - local file="$3" - - # 转义特殊字符 - local key_escaped=$(sed 's/[\/&]/\\&/g' <<< "$key") - local value_escaped=$(sed 's/[\/&]/\\&/g' <<< "$value") - - if [ ! -f "$file" ]; then - log_error "文件不存在: $file" - return 1 - fi - - # 检查并移除chattr只读属性(如果存在) - if lsattr "$file" 2>/dev/null | grep -q '^....i'; then - log_debug "移除文件不可变属性..." - sudo chattr -i "$file" || { - log_error "无法移除文件不可变属性" - return 1 - } - fi - - # 确保文件可写 - chmod 644 "$file" || { - log_error "无法修改文件权限: $file" - return 1 - } - - # 创建临时文件 - local temp_file=$(mktemp) - - # 检查key是否存在 - if grep -q "\"$key\":" "$file"; then - # 使用#作为分隔符避免冲突,并转义特殊字符 - sed "s#\"${key_escaped}\":[[:space:]]*\"[^\"]*\"#\"${key_escaped}\": \"${value_escaped}\"#" "$file" > "$temp_file" || { - log_error "修改配置失败: $key" - rm -f "$temp_file" - return 1 - } - else - # 添加新键值对时转义特殊字符 - sed "s/}$/,\n \"${key_escaped}\": \"${value_escaped}\"\n}/" "$file" > "$temp_file" || { - log_error "添加配置失败: $key" - rm -f "$temp_file" - return 1 - } - fi - - # 检查临时文件是否为空 - if [ ! -s "$temp_file" ]; then - log_error "生成的临时文件为空" - rm -f "$temp_file" - return 1 - fi - - # 使用 cat 替换原文件内容 - cat "$temp_file" > "$file" || { - log_error "无法写入文件: $file" - rm -f "$temp_file" - return 1 - } - - rm -f "$temp_file" - - # 恢复文件权限 - chmod 444 "$file" - +# 安全终止进程 +terminate_cursor() { + log "INFO" "光标进程搜索..." + local pids=$(get_cursor_pids) + + if [ -z "$pids" ]; then + log "INFO" "未发现任何 Cursor 进程" return 0 -} + fi -# 生成新的配置 -generate_new_config() { - # 修改系统 ID - log_info "正在修改系统 ID..." - - # 备份当前系统 ID - backup_system_id - - # 生成新的 machine-id - local new_machine_id=$(generate_random_id | cut -c1-32) - - # 备份并修改 machine-id - if [ -f "/etc/machine-id" ]; then - cp /etc/machine-id /etc/machine-id.backup - echo "$new_machine_id" > /etc/machine-id - log_info "系统 machine-id 已更新" - fi - - # 将 auth0|user_ 转换为字节数组的十六进制 - local prefix_hex=$(echo -n "auth0|user_" | xxd -p) - local random_part=$(generate_random_id) - local machine_id="${prefix_hex}${random_part}" - - local mac_machine_id=$(generate_random_id) - local device_id=$(generate_uuid | tr '[:upper:]' '[:lower:]') - local sqm_id="{$(generate_uuid | tr '[:lower:]' '[:upper:]')}" - - log_info "正在修改配置文件..." - # 检查配置文件是否存在 - if [ ! -f "$STORAGE_FILE" ]; then - log_error "未找到配置文件: $STORAGE_FILE" - log_warn "请先安装并运行一次 Cursor 后再使用此脚本" - exit 1 - fi - - # 确保配置文件目录存在 - mkdir -p "$(dirname "$STORAGE_FILE")" || { - log_error "无法创建配置目录" - exit 1 - } - - # 如果文件不存在,创建一个基本的 JSON 结构 - if [ ! -s "$STORAGE_FILE" ]; then - echo '{}' > "$STORAGE_FILE" || { - log_error "无法初始化配置文件" - exit 1 - } - fi - - # 修改现有文件 - modify_or_add_config "telemetry.machineId" "$machine_id" "$STORAGE_FILE" || exit 1 - modify_or_add_config "telemetry.macMachineId" "$mac_machine_id" "$STORAGE_FILE" || exit 1 - modify_or_add_config "telemetry.devDeviceId" "$device_id" "$STORAGE_FILE" || exit 1 - modify_or_add_config "telemetry.sqmId" "$sqm_id" "$STORAGE_FILE" || exit 1 - - # 设置文件权限和所有者 - chmod 444 "$STORAGE_FILE" # 改为只读权限 - chown "$CURRENT_USER" "$STORAGE_FILE" - - # 验证权限设置 - if [ -w "$STORAGE_FILE" ]; then - log_warn "无法设置只读权限,尝试使用其他方法..." - chattr +i "$STORAGE_FILE" 2>/dev/null || true - else - log_info "成功设置文件只读权限" - fi - - echo - log_info "已更新配置: $STORAGE_FILE" - log_debug "machineId: $machine_id" - log_debug "macMachineId: $mac_machine_id" - log_debug "devDeviceId: $device_id" - log_debug "sqmId: $sqm_id" -} + log "WARN" "发现的过程 Cursor (PID): $pids" + echo "过程详情:" + ps -fp $pids -# 显示文件树结构 -show_file_tree() { - local base_dir=$(dirname "$STORAGE_FILE") - echo - log_info "文件结构:" - echo -e "${BLUE}$base_dir${NC}" - echo "├── globalStorage" - echo "│ ├── storage.json (已修改)" - echo "│ └── backups" - - # 列出备份文件 - if [ -d "$BACKUP_DIR" ]; then - local backup_files=("$BACKUP_DIR"/*) - if [ ${#backup_files[@]} -gt 0 ]; then - for file in "${backup_files[@]}"; do - if [ -f "$file" ]; then - echo "│ └── $(basename "$file")" - fi - done - else - echo "│ └── (空)" - fi - fi - echo -} + read -r -p "您确定要终止这些进程吗? [y/N] " response /dev/null || true -# 显示公众号信息 -show_follow_info() { - echo - echo -e "${GREEN}================================${NC}" - echo -e "${YELLOW} 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) ${NC}" - echo -e "${GREEN}================================${NC}" - echo -} + for i in {1..10}; do + sleep 0.5 + local remaining=$(get_cursor_pids) + [ -z "$remaining" ] && break + done -# 修改 disable_auto_update 函数,在失败处理时添加手动教程 -disable_auto_update() { - echo - log_warn "是否要禁用 Cursor 自动更新功能?" - echo "0) 否 - 保持默认设置 (按回车键)" - echo "1) 是 - 禁用自动更新" - read -r choice - - if [ "$choice" = "1" ]; then - echo - log_info "正在处理自动更新..." - local updater_path="$HOME/.config/cursor-updater" - - # 定义手动设置教程 - show_manual_guide() { - echo - log_warn "自动设置失败,请尝试手动操作:" - echo -e "${YELLOW}手动禁用更新步骤:${NC}" - echo "1. 打开终端" - echo "2. 复制粘贴以下命令:" - echo -e "${BLUE}rm -rf \"$updater_path\" && touch \"$updater_path\" && chmod 444 \"$updater_path\"${NC}" - echo - echo -e "${YELLOW}如果上述命令提示权限不足,请使用 sudo:${NC}" - echo -e "${BLUE}sudo rm -rf \"$updater_path\" && sudo touch \"$updater_path\" && sudo chmod 444 \"$updater_path\"${NC}" - echo - echo -e "${YELLOW}如果要添加额外保护(推荐),请执行:${NC}" - echo -e "${BLUE}sudo chattr +i \"$updater_path\"${NC}" - echo - echo -e "${YELLOW}验证方法:${NC}" - echo "1. 运行命令:ls -l \"$updater_path\"" - echo "2. 确认文件权限为 r--r--r--" - echo "3. 运行命令:lsattr \"$updater_path\"" - echo "4. 确认有 'i' 属性(如果执行了 chattr 命令)" - echo - log_warn "完成后请重启 Cursor" - } - - if [ -d "$updater_path" ]; then - rm -rf "$updater_path" 2>/dev/null || { - log_error "删除 cursor-updater 目录失败" - show_manual_guide - return 1 - } - log_info "成功删除 cursor-updater 目录" - fi - - touch "$updater_path" 2>/dev/null || { - log_error "创建阻止文件失败" - show_manual_guide - return 1 - } - - if ! chmod 444 "$updater_path" 2>/dev/null || ! chown "$CURRENT_USER:$CURRENT_USER" "$updater_path" 2>/dev/null; then - log_error "设置文件权限失败" - show_manual_guide - return 1 - fi - - # 尝试设置不可修改属性 - if command -v chattr &> /dev/null; then - chattr +i "$updater_path" 2>/dev/null || { - log_warn "chattr 设置失败" - show_manual_guide - return 1 - } - fi - - # 验证设置是否成功 - if [ ! -f "$updater_path" ] || [ -w "$updater_path" ]; then - log_error "验证失败:文件权限设置可能未生效" - show_manual_guide - return 1 - fi - - log_info "成功禁用自动更新" - else - log_info "保持默认设置,不进行更改" + local remaining=$(get_cursor_pids) + if [ -n "$remaining" ]; then + log "WARN" "强制完成..." + kill -KILL $remaining 2>/dev/null || true fi -} -# 主函数 + log "INFO" "成功完成的流程" + else + log "ERROR" "用户取消" + exit 1 + fi +} + +# 备份 +backup_files() { + local user_home=$1 + local cursor_dir_name=$(basename $(find_cursor_dir)) + local config_dir="$user_home/.config/$cursor_dir_name/User/globalStorage" + local storage_file="$config_dir/storage.json" + local backup_dir="$config_dir/backups" + + mkdir -p "$backup_dir" + local timestamp=$(date +%Y%m%d_%H%M%S) + + if [ -f "$storage_file" ]; then + cp "$storage_file" "$backup_dir/storage.json.bak_$timestamp" + log "INFO" "配置已备份: $backup_dir/storage.json.bak_$timestamp" + fi + + local machine_id="/etc/machine-id" + if [ -f "$machine_id" ]; then + cp "$machine_id" "$backup_dir/machine-id.bak_$timestamp" + log "INFO" "创建了备份机器 ID" + fi +} + +# 生成新的 ID +generate_ids() { + local user_home=$1 + local cursor_dir_name=$(basename $(find_cursor_dir)) + local config_file="$user_home/.config/$cursor_dir_name/User/globalStorage/storage.json" + + local new_machine_id=$(uuidgen | tr -d '-') + local new_device_id=$(uuidgen) + local new_sqm_id="$(uuidgen | tr '[:lower:]' '[:upper:]')" + + log "DEBUG" "生成新的 ID:" + log "DEBUG" "Machine ID: $new_machine_id" + log "DEBUG" "Device ID: $new_device_id" + log "DEBUG" "SQM ID: $new_sqm_id" + + if [ ! -f "$config_file" ]; then + mkdir -p "$(dirname "$config_file")" + echo '{}' > "$config_file" + fi + + echo "$new_machine_id" | tr -d '-' | cut -c1-32 > "/etc/machine-id" + + tmp_file=$(mktemp) + jq --arg machine "$new_machine_id" \ + --arg device "$new_device_id" \ + --arg sqm "{$new_sqm_id}" \ + '.telemetry.machineId = $machine | + .telemetry.devDeviceId = $device | + .telemetry.sqmId = $sqm' "$config_file" > "$tmp_file" + + mv "$tmp_file" "$config_file" + chmod 444 "$config_file" + chattr +i "$config_file" 2>/dev/null || true +} + +# 关闭更新 +disable_updates() { + local user_home=$1 + local updater_dir="$user_home/.config/cursor-updater" + + log "WARN" "设置更新..." + echo -e "选择一项操作: \n1) 禁用更新 \n2) 保持不变" + read -r -p "您的选择 [1/2: " choice /dev/null || log "WARN" "阻止更新失败" + log "INFO" "禁用自动更新" + fi +} + +# 主要功能 main() { - # 检查是否为 Linux 系统 - if [[ $(uname) != "Linux" ]]; then - log_error "本脚本仅支持 Linux 系统" - exit 1 - fi - - clear - # 显示 Logo - echo -e " - ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ + check_permissions + clear + + echo -e " + ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ ██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗ ██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝ ██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗ ╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ - " - echo -e "${BLUE}================================${NC}" - echo -e "${GREEN} Cursor 设备ID 修改工具 (Linux版) ${NC}" - echo -e "${YELLOW} 关注公众号【煎饼果子卷AI】 ${NC}" - echo -e "${YELLOW} 一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) ${NC}" - echo -e "${BLUE}================================${NC}" - echo - echo -e "${YELLOW}[重要提示]${NC} 本工具支持 Cursor v0.47.x" - echo -e "${YELLOW}[重要提示]${NC} 本工具免费,如果对您有帮助,请关注公众号【煎饼果子卷AI】" - echo - - check_permissions - check_and_kill_cursor - backup_config - generate_new_config - show_file_tree - show_follow_info - - # 添加禁用自动更新功能 - disable_auto_update - - log_info "请重启 Cursor 以应用新的配置" - show_follow_info + " + echo -e "${BLUE}================================${NC}" + echo -e "${GREEN} Cursor 设备ID 修改工具 (Linux版) ${NC}" + echo -e "${YELLOW} 关注公众号【煎饼果子卷AI】 ${NC}" + echo -e "${YELLOW} 一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) ${NC}" + echo -e "${BLUE}================================${NC}" + echo + echo -e "${YELLOW}[重要提示]${NC} 本工具支持 Cursor v0.47.x" + echo -e "${YELLOW}[重要提示]${NC} 本工具免费,如果对您有帮助,请关注公众号【煎饼果子卷AI】" + echo + + local current_user=$(logname 2>/dev/null || echo "$SUDO_USER") + local user_home=$(getent passwd "$current_user" | cut -d: -f6) + + terminate_cursor + backup_files "$user_home" + generate_ids "$user_home" + disable_updates "$user_home" + + log "INFO" "操作完成! 请重新启动Cursor" } -# 执行主函数 -main +main "$@"