From d01dcdb7ba479543cb3c848c1dc58576019dc4b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=85=8E=E9=A5=BC=E6=9E=9C=E5=AD=90=E5=8D=B7=E9=B2=A8?= =?UTF-8?q?=E9=B1=BC=E8=BE=A3=E6=A4=92?= Date: Wed, 30 Apr 2025 12:31:45 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20Cursor=20=E5=AE=89?= =?UTF-8?q?=E8=A3=85=E7=9B=B8=E5=85=B3=E5=8A=9F=E8=83=BD=EF=BC=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E4=BA=86=E6=9F=A5=E6=89=BE=E5=92=8C=E5=AE=89=E8=A3=85?= =?UTF-8?q?=E8=BF=87=E7=A8=8B=E3=80=82=E5=8C=85=E6=8B=AC=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=A0=87=E5=87=86=E5=AE=89=E8=A3=85=E8=B7=AF=E5=BE=84=E3=80=81?= =?UTF-8?q?=E6=94=B9=E8=BF=9B=E8=B5=84=E6=BA=90=E7=9B=AE=E5=BD=95=E6=9F=A5?= =?UTF-8?q?=E6=89=BE=E9=80=BB=E8=BE=91=E3=80=81=E5=AE=9E=E7=8E=B0=E4=BB=8E?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=20AppImage=20=E5=AE=89=E8=A3=85=20Cursor=20?= =?UTF-8?q?=E7=9A=84=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=B9=B6=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E4=BA=86=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95=E4=BB=A5=E6=8F=90?= =?UTF-8?q?=E9=AB=98=E7=94=A8=E6=88=B7=E4=BD=93=E9=AA=8C=E3=80=82=E5=90=8C?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E6=9B=B4=E6=96=B0=E4=BA=86=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E7=A1=AE=E4=BF=9D=E5=9C=A8=E5=A4=87=E4=BB=BD=E5=92=8C=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E8=BF=87=E7=A8=8B=E4=B8=AD=E6=9B=B4=E5=85=B7=E5=8F=AF?= =?UTF-8?q?=E9=9D=A0=E6=80=A7=E3=80=82=E6=AD=A4=E6=9B=B4=E6=94=B9=E6=97=A8?= =?UTF-8?q?=E5=9C=A8=E6=8F=90=E5=8D=87=E8=84=9A=E6=9C=AC=E7=9A=84=E7=A8=B3?= =?UTF-8?q?=E5=AE=9A=E6=80=A7=E5=92=8C=E7=94=A8=E6=88=B7=E5=8F=8B=E5=A5=BD?= =?UTF-8?q?=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/run/cursor_linux_id_modifier.sh | 1069 +++++++++++++++-------- 1 file changed, 728 insertions(+), 341 deletions(-) diff --git a/scripts/run/cursor_linux_id_modifier.sh b/scripts/run/cursor_linux_id_modifier.sh index 71ed2aa..26494d6 100755 --- a/scripts/run/cursor_linux_id_modifier.sh +++ b/scripts/run/cursor_linux_id_modifier.sh @@ -70,12 +70,20 @@ CURSOR_CONFIG_DIR="$HOME/.config/Cursor" STORAGE_FILE="$CURSOR_CONFIG_DIR/User/globalStorage/storage.json" BACKUP_DIR="$CURSOR_CONFIG_DIR/User/globalStorage/backups" -# 可能的Cursor二进制路径 +# --- 新增:安装相关变量 --- +APPIMAGE_SEARCH_DIR="/opt/CursorInstall" # AppImage 搜索目录,可按需修改 +APPIMAGE_PATTERN="Cursor-*.AppImage" # AppImage 文件名模式 +INSTALL_DIR="/opt/Cursor" # Cursor 最终安装目录 +ICON_PATH="/usr/share/icons/cursor.png" +DESKTOP_FILE="/usr/share/applications/cursor-cursor.desktop" +# --- 结束:安装相关变量 --- + +# 可能的Cursor二进制路径 - 添加了标准安装路径 CURSOR_BIN_PATHS=( "/usr/bin/cursor" "/usr/local/bin/cursor" + "$INSTALL_DIR/cursor" # 添加标准安装路径 "$HOME/.local/bin/cursor" - "/opt/cursor/cursor" "/snap/bin/cursor" ) @@ -84,7 +92,7 @@ find_cursor_path() { log_info "查找Cursor安装路径..." for path in "${CURSOR_BIN_PATHS[@]}"; do - if [ -f "$path" ]; then + if [ -f "$path" ] && [ -x "$path" ]; then # 确保文件存在且可执行 log_info "找到Cursor安装路径: $path" CURSOR_PATH="$path" return 0 @@ -98,15 +106,21 @@ find_cursor_path() { return 0 fi - # 尝试查找可能的安装路径 - local cursor_paths=$(find /usr /opt $HOME/.local -name "cursor" -type f -executable 2>/dev/null) + # 尝试查找可能的安装路径 (限制搜索范围和类型) + local cursor_paths=$(find /usr /opt $HOME/.local -path "$INSTALL_DIR/cursor" -o -name "cursor" -type f -executable 2>/dev/null) if [ -n "$cursor_paths" ]; then - CURSOR_PATH=$(echo "$cursor_paths" | head -1) + # 优先选择标准安装路径 + local standard_path=$(echo "$cursor_paths" | grep "$INSTALL_DIR/cursor" | head -1) + if [ -n "$standard_path" ]; then + CURSOR_PATH="$standard_path" + else + CURSOR_PATH=$(echo "$cursor_paths" | head -1) + fi log_info "通过查找找到Cursor: $CURSOR_PATH" return 0 fi - log_warn "未找到Cursor可执行文件,将尝试使用配置目录" + log_warn "未找到Cursor可执行文件" return 1 } @@ -114,27 +128,41 @@ find_cursor_path() { find_cursor_resources() { log_info "查找Cursor资源目录..." - # 可能的资源目录路径 + # 可能的资源目录路径 - 添加了标准安装目录 local resource_paths=( + "$INSTALL_DIR" # 添加标准安装路径 "/usr/lib/cursor" "/usr/share/cursor" - "/opt/cursor" "$HOME/.local/share/cursor" ) for path in "${resource_paths[@]}"; do - if [ -d "$path" ]; then + if [ -d "$path/resources" ]; then # 检查是否存在 resources 子目录 log_info "找到Cursor资源目录: $path" CURSOR_RESOURCES="$path" return 0 fi + if [ -d "$path/app" ]; then # 有些版本可能直接是 app 目录 + log_info "找到Cursor资源目录 (app): $path" + CURSOR_RESOURCES="$path" + return 0 + fi done # 如果有CURSOR_PATH,尝试从它推断 if [ -n "$CURSOR_PATH" ]; then local base_dir=$(dirname "$CURSOR_PATH") + # 检查常见的相对路径 if [ -d "$base_dir/resources" ]; then - CURSOR_RESOURCES="$base_dir/resources" + CURSOR_RESOURCES="$base_dir" + log_info "通过二进制路径找到资源目录: $CURSOR_RESOURCES" + return 0 + elif [ -d "$base_dir/../resources" ]; then # 例如在 bin 目录内 + CURSOR_RESOURCES=$(realpath "$base_dir/..") + log_info "通过二进制路径找到资源目录: $CURSOR_RESOURCES" + return 0 + elif [ -d "$base_dir/../lib/cursor/resources" ]; then # 另一种常见结构 + CURSOR_RESOURCES=$(realpath "$base_dir/../lib/cursor") log_info "通过二进制路径找到资源目录: $CURSOR_RESOURCES" return 0 fi @@ -147,12 +175,166 @@ find_cursor_resources() { # 检查权限 check_permissions() { if [ "$EUID" -ne 0 ]; then - log_error "请使用 sudo 运行此脚本" + log_error "请使用 sudo 运行此脚本 (安装和修改系统文件需要权限)" echo "示例: sudo $0" exit 1 fi } +# --- 新增/重构:从本地 AppImage 安装 Cursor --- +install_cursor_appimage() { + log_info "开始尝试从本地 AppImage 安装 Cursor..." + local found_appimage_path="" + + # 确保搜索目录存在 + mkdir -p "$APPIMAGE_SEARCH_DIR" + + # 查找 AppImage 文件 + find_appimage() { + found_appimage_path=$(find "$APPIMAGE_SEARCH_DIR" -maxdepth 1 -name "$APPIMAGE_PATTERN" -print -quit) + if [ -z "$found_appimage_path" ]; then + return 1 + else + return 0 + fi + } + + if ! find_appimage; then + log_warn "在 '$APPIMAGE_SEARCH_DIR' 目录下未找到 '$APPIMAGE_PATTERN' 文件。" + # --- 新增:添加文件名格式提醒 --- + log_info "请确保 AppImage 文件名格式类似: Cursor-版本号-架构.AppImage (例如: Cursor-0.49.6-aarch64.AppImage 或 Cursor-x.y.z-x86_64.AppImage)" + # --- 结束:添加文件名格式提醒 --- + # 等待用户放置文件 + read -p $"请将 Cursor AppImage 文件放入 '$APPIMAGE_SEARCH_DIR' 目录,然后按 Enter 键继续..." + + # 再次查找 + if ! find_appimage; then + log_error "在 '$APPIMAGE_SEARCH_DIR' 中仍然找不到 '$APPIMAGE_PATTERN' 文件。安装中止。" + return 1 + fi + fi + + log_info "找到 AppImage 文件: $found_appimage_path" + local appimage_filename=$(basename "$found_appimage_path") + + # 进入搜索目录操作,避免路径问题 + local current_dir=$(pwd) + cd "$APPIMAGE_SEARCH_DIR" || { log_error "无法进入目录: $APPIMAGE_SEARCH_DIR"; return 1; } + + log_info "设置 '$appimage_filename' 可执行权限..." + chmod +x "$appimage_filename" || { + log_error "设置可执行权限失败: $appimage_filename" + cd "$current_dir" + return 1 + } + + log_info "解压 AppImage 文件 '$appimage_filename'..." + # 创建临时解压目录 + local extract_dir="squashfs-root" + rm -rf "$extract_dir" # 清理旧的解压目录(如果存在) + + # 执行解压,将输出重定向避免干扰 + if ./"$appimage_filename" --appimage-extract > /dev/null; then + log_info "AppImage 解压成功到 '$extract_dir'" + else + log_error "解压 AppImage 失败: $appimage_filename" + rm -rf "$extract_dir" # 清理失败的解压 + cd "$current_dir" + return 1 + fi + + # 检查解压后的预期目录结构 + local cursor_source_dir="" + if [ -d "$extract_dir/usr/share/cursor" ]; then + cursor_source_dir="$extract_dir/usr/share/cursor" + elif [ -d "$extract_dir" ]; then # 有些 AppImage 可能直接在根目录 + # 进一步检查是否存在关键文件/目录 + if [ -f "$extract_dir/cursor" ] && [ -d "$extract_dir/resources" ]; then + cursor_source_dir="$extract_dir" + fi + fi + + if [ -z "$cursor_source_dir" ]; then + log_error "解压后的目录 '$extract_dir' 中未找到预期的 Cursor 文件结构 (例如 'usr/share/cursor' 或直接包含 'cursor' 和 'resources')。" + rm -rf "$extract_dir" + cd "$current_dir" + return 1 + fi + log_info "找到 Cursor 源文件在: $cursor_source_dir" + + + log_info "安装 Cursor 到 '$INSTALL_DIR'..." + # 如果安装目录已存在,先删除 (确保全新安装) + if [ -d "$INSTALL_DIR" ]; then + log_warn "发现已存在的安装目录 '$INSTALL_DIR',将先移除..." + rm -rf "$INSTALL_DIR" || { log_error "移除旧安装目录失败: $INSTALL_DIR"; cd "$current_dir"; return 1; } + fi + + # 创建安装目录的父目录(如果需要)并设置权限 + mkdir -p "$(dirname "$INSTALL_DIR")" + + # 移动解压后的内容到安装目录 + if mv "$cursor_source_dir" "$INSTALL_DIR"; then + log_info "成功将文件移动到 '$INSTALL_DIR'" + # 确保安装目录及其内容归属当前用户(如果需要) + chown -R "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$INSTALL_DIR" || log_warn "设置 '$INSTALL_DIR' 文件所有权失败,可能需要手动调整" + chmod -R u+rwX,go+rX,go-w "$INSTALL_DIR" || log_warn "设置 '$INSTALL_DIR' 文件权限失败,可能需要手动调整" + else + log_error "移动文件到安装目录 '$INSTALL_DIR' 失败" + rm -rf "$extract_dir" # 确保清理 + rm -rf "$INSTALL_DIR" # 清理部分移动的文件 + cd "$current_dir" + return 1 + fi + + # 处理图标和桌面快捷方式 (从脚本执行的原始目录查找) + cd "$current_dir" # 返回原始目录查找图标等文件 + + local icon_source="./cursor.png" + local desktop_source="./cursor-cursor.desktop" + + if [ -f "$icon_source" ]; then + log_info "安装图标..." + mkdir -p "$(dirname "$ICON_PATH")" + cp "$icon_source" "$ICON_PATH" || log_warn "无法复制图标文件 '$icon_source' 到 '$ICON_PATH'" + chmod 644 "$ICON_PATH" || log_warn "设置图标文件权限失败: $ICON_PATH" + else + log_warn "图标文件 '$icon_source' 在脚本当前目录不存在,跳过图标安装。" + log_warn "请将 'cursor.png' 文件放置在脚本目录 '$current_dir' 下并重新运行安装(如果需要图标)。" + fi + + if [ -f "$desktop_source" ]; then + log_info "安装桌面快捷方式..." + mkdir -p "$(dirname "$DESKTOP_FILE")" + cp "$desktop_source" "$DESKTOP_FILE" || log_warn "无法创建桌面快捷方式 '$desktop_source' 到 '$DESKTOP_FILE'" + chmod 644 "$DESKTOP_FILE" || log_warn "设置桌面文件权限失败: $DESKTOP_FILE" + + # 更新桌面数据库 + log_info "更新桌面数据库..." + update-desktop-database "$(dirname "$DESKTOP_FILE")" &> /dev/null || log_warn "无法更新桌面数据库,快捷方式可能不会立即显示" + else + log_warn "桌面文件 '$desktop_source' 在脚本当前目录不存在,跳过快捷方式安装。" + log_warn "请将 'cursor-cursor.desktop' 文件放置在脚本目录 '$current_dir' 下并重新运行安装(如果需要快捷方式)。" + fi + + # 创建符号链接到 /usr/local/bin + log_info "创建命令行启动链接..." + ln -sf "$INSTALL_DIR/cursor" /usr/local/bin/cursor || log_warn "无法创建命令行链接 '/usr/local/bin/cursor'" + + # 清理临时文件 + log_info "清理临时文件..." + cd "$APPIMAGE_SEARCH_DIR" # 返回搜索目录清理 + rm -rf "$extract_dir" + log_info "正在删除原始 AppImage 文件: $found_appimage_path" + rm -f "$appimage_filename" # 删除 AppImage 文件 + + cd "$current_dir" # 确保返回最终目录 + + log_info "Cursor 安装成功!安装目录: $INSTALL_DIR" + return 0 +} +# --- 结束:安装函数 --- + # 检查并关闭 Cursor 进程 check_and_kill_cursor() { log_info "检查 Cursor 进程..." @@ -209,7 +391,7 @@ check_and_kill_cursor() { # 备份配置文件 backup_config() { if [ ! -f "$STORAGE_FILE" ]; then - log_warn "配置文件不存在,跳过备份" + log_warn "配置文件 '$STORAGE_FILE' 不存在,跳过备份" return 0 fi @@ -218,12 +400,14 @@ backup_config() { if cp "$STORAGE_FILE" "$backup_file"; then chmod 644 "$backup_file" - chown "$CURRENT_USER" "$backup_file" + # 确保备份文件归属正确用户 + chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$backup_file" || log_warn "设置备份文件所有权失败: $backup_file" log_info "配置已备份到: $backup_file" else - log_error "备份失败" + log_error "备份失败: $STORAGE_FILE" exit 1 fi + return 0 # 明确返回成功 } # 生成随机 ID @@ -243,7 +427,7 @@ generate_uuid() { cat /proc/sys/kernel/random/uuid else # 最后备选方案:使用openssl生成 - openssl rand -hex 16 | sed 's/\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)/\1\2\3\4-\5\6-\7\8-\9\10-\11\12\13\14\15\16/' + openssl rand -hex 16 | sed 's/\\(..\\)\\(..\\)\\(..\\)\\(..\\)\\(..\\)\\(..\\)\\(..\\)\\(..\\)/\\1\\2\\3\\4-\\5\\6-\\7\\8-\\9\\10-\\11\\12\\13\\14\\15\\16/' fi fi } @@ -255,13 +439,13 @@ modify_or_add_config() { local file="$3" if [ ! -f "$file" ]; then - log_error "文件不存在: $file" + log_error "配置文件不存在: $file" return 1 fi - # 确保文件可写 - chmod 644 "$file" || { - log_error "无法修改文件权限: $file" + # 确保文件对当前执行用户(root)可写 + chmod u+w "$file" || { + log_error "无法修改文件权限(写): $file" return 1 } @@ -269,40 +453,54 @@ modify_or_add_config() { local temp_file=$(mktemp) # 检查key是否存在 - if grep -q "\"$key\":" "$file"; then - # key存在,执行替换 - sed "s/\"$key\":[[:space:]]*\"[^\"]*\"/\"$key\": \"$value\"/" "$file" > "$temp_file" || { - log_error "修改配置失败: $key" + if grep -q "\"$key\":[[:space:]]*\"[^\"]*\"" "$file"; then + # key存在,执行替换 (更精确的匹配) + sed "s/\\(\"$key\"\\):[[:space:]]*\"[^\"]*\"/\\1: \"$value\"/" "$file" > "$temp_file" || { + log_error "修改配置失败 (替换): $key in $file" rm -f "$temp_file" + chmod u-w "$file" # 恢复权限 return 1 } + log_debug "已替换 key '$key' 在文件 '$file' 中" + elif grep -q "}" "$file"; then + # key不存在, 在最后一个 '}' 前添加新的key-value对 + # 注意:这种方式比较脆弱,如果 JSON 格式不标准或最后一行不是 '}' 会失败 + sed '$ s/}/,\n "'$key'\": "'$value'\"\n}/' "$file" > "$temp_file" || { + log_error "添加配置失败 (注入): $key to $file" + rm -f "$temp_file" + chmod u-w "$file" # 恢复权限 + return 1 + } + log_debug "已添加 key '$key' 到文件 '$file' 中" else - # key不存在,添加新的key-value对 - sed "s/}$/,\n \"$key\": \"$value\"\n}/" "$file" > "$temp_file" || { - log_error "添加配置失败: $key" - rm -f "$temp_file" - return 1 - } + log_error "无法确定如何添加配置: $key to $file (文件结构可能不标准)" + rm -f "$temp_file" + chmod u-w "$file" # 恢复权限 + return 1 fi - - # 检查临时文件是否为空 + + # 检查临时文件是否有效 if [ ! -s "$temp_file" ]; then - log_error "生成的临时文件为空" + log_error "修改或添加配置后生成的临时文件为空: $key in $file" rm -f "$temp_file" + chmod u-w "$file" # 恢复权限 return 1 fi # 使用 cat 替换原文件内容 cat "$temp_file" > "$file" || { - log_error "无法写入文件: $file" + log_error "无法写入更新后的配置到文件: $file" rm -f "$temp_file" + # 尝试恢复权限(如果失败也无大碍) + chmod u-w "$file" || true return 1 } rm -f "$temp_file" - # 恢复文件权限 - chmod 444 "$file" + # 设置所有者和基础权限(root执行时目标文件是用户家目录下的) + chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$file" || log_warn "设置文件所有权失败: $file" + chmod 644 "$file" || log_warn "设置文件权限失败: $file" # 用户读写,组和其他读 return 0 } @@ -319,21 +517,29 @@ generate_new_config() { # 记录日志以便调试 echo "[INPUT_DEBUG] 机器码重置选项选择: $reset_choice" >> "$LOG_FILE" + # 确保配置文件目录存在 + mkdir -p "$(dirname "$STORAGE_FILE")" + chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$(dirname "$STORAGE_FILE")" || log_warn "设置配置目录所有权失败: $(dirname "$STORAGE_FILE")" + chmod 755 "$(dirname "$STORAGE_FILE")" || log_warn "设置配置目录权限失败: $(dirname "$STORAGE_FILE")" + # 处理用户选择 - 索引0对应"不重置"选项,索引1对应"重置"选项 if [ "$reset_choice" = "1" ]; then log_info "您选择了重置机器码" - # 确保配置文件目录存在 + # 检查配置文件是否存在 if [ -f "$STORAGE_FILE" ]; then log_info "发现已有配置文件: $STORAGE_FILE" - # 备份现有配置(以防万一) - backup_config + # 备份现有配置 + if ! backup_config; then # 如果备份失败,不继续修改 + log_error "配置文件备份失败,中止机器码重置。" + return 1 # 返回错误状态 + fi # 生成并设置新的设备ID local new_device_id=$(generate_uuid) - local new_machine_id="auth0|user_$(openssl rand -hex 16)" - + local new_machine_id=$(generate_uuid) # 使用 UUID 作为 Machine ID 更常见 + log_info "正在设置新的设备和机器ID..." log_debug "新设备ID: $new_device_id" log_debug "新机器ID: $new_machine_id" @@ -341,29 +547,34 @@ generate_new_config() { # 修改配置文件 if modify_or_add_config "deviceId" "$new_device_id" "$STORAGE_FILE" && \ modify_or_add_config "machineId" "$new_machine_id" "$STORAGE_FILE"; then - log_info "配置文件修改成功" + log_info "配置文件中的 deviceId 和 machineId 修改成功" else - log_error "配置文件修改失败" + log_error "配置文件中的 deviceId 或 machineId 修改失败" + # 注意:即使失败,备份仍在,但配置文件可能已部分修改 + return 1 # 返回错误状态 fi else - log_warn "未找到配置文件,这是正常的,脚本将跳过ID修改" + log_warn "未找到配置文件 '$STORAGE_FILE',无法重置机器码。如果这是首次安装,这是正常的。" + # 即使文件不存在,也认为此步骤(不执行)是"成功"的,允许继续 fi else log_info "您选择了不重置机器码,将仅修改js文件" - # 确保配置文件目录存在 + # 检查配置文件是否存在并备份(如果存在) if [ -f "$STORAGE_FILE" ]; then log_info "发现已有配置文件: $STORAGE_FILE" - - # 备份现有配置(以防万一) - backup_config + if ! backup_config; then + log_error "配置文件备份失败,中止操作。" + return 1 # 返回错误状态 + fi else - log_warn "未找到配置文件,这是正常的,脚本将跳过ID修改" + log_warn "未找到配置文件 '$STORAGE_FILE',跳过备份。" fi fi echo log_info "配置处理完成" + return 0 # 明确返回成功 } # 查找Cursor的JS文件 @@ -373,68 +584,69 @@ find_cursor_js_files() { local js_files=() local found=false - # 如果找到了资源目录,在资源目录中搜索 - if [ -n "$CURSOR_RESOURCES" ]; then - log_debug "在资源目录中搜索JS文件: $CURSOR_RESOURCES" - - # 在资源目录中递归搜索特定JS文件 - local js_patterns=( - "*/extensionHostProcess.js" - "*/main.js" - "*/cliProcessMain.js" - "*/app/out/vs/workbench/api/node/extensionHostProcess.js" - "*/app/out/main.js" - "*/app/out/vs/code/node/cliProcessMain.js" - ) - - for pattern in "${js_patterns[@]}"; do - local files=$(find "$CURSOR_RESOURCES" -path "$pattern" -type f 2>/dev/null) - if [ -n "$files" ]; then - while read -r file; do + # 确保 CURSOR_RESOURCES 已设置 + if [ -z "$CURSOR_RESOURCES" ] || [ ! -d "$CURSOR_RESOURCES" ]; then + log_error "Cursor 资源目录未找到或无效 ($CURSOR_RESOURCES),无法查找 JS 文件。" + return 1 + fi + + log_debug "在资源目录中搜索JS文件: $CURSOR_RESOURCES" + + # 在资源目录中递归搜索特定JS文件 + # 注意:这些模式可能需要根据 Cursor 版本更新 + local js_patterns=( + "resources/app/out/vs/workbench/api/node/extensionHostProcess.js" + "resources/app/out/main.js" + "resources/app/out/vs/code/node/cliProcessMain.js" + # 添加其他可能的路径模式 + "app/out/vs/workbench/api/node/extensionHostProcess.js" # 如果资源目录是 app 的父目录 + "app/out/main.js" + "app/out/vs/code/node/cliProcessMain.js" + ) + + for pattern in "${js_patterns[@]}"; do + # 使用 find 在 CURSOR_RESOURCES 下查找完整路径 + local files=$(find "$CURSOR_RESOURCES" -path "*/$pattern" -type f 2>/dev/null) + if [ -n "$files" ]; then + while IFS= read -r file; do + # 检查文件是否已添加 + if [[ ! " ${js_files[@]} " =~ " ${file} " ]]; then log_info "找到JS文件: $file" js_files+=("$file") found=true - done <<< "$files" - fi - done - fi + fi + done <<< "$files" + fi + done - # 如果还没找到,尝试在/usr和$HOME目录下搜索 + # 如果还没找到,尝试更通用的搜索(可能误报) if [ "$found" = false ]; then - log_warn "在资源目录中未找到JS文件,尝试在其他目录中搜索..." - - # 在系统目录中搜索,限制深度以避免过长搜索 - local search_dirs=( - "/usr/lib/cursor" - "/usr/share/cursor" - "/opt/cursor" - "$HOME/.config/Cursor" - "$HOME/.local/share/cursor" - ) - - for dir in "${search_dirs[@]}"; do - if [ -d "$dir" ]; then - log_debug "搜索目录: $dir" - local files=$(find "$dir" -name "*.js" -type f -exec grep -l "IOPlatformUUID\|x-cursor-checksum" {} \; 2>/dev/null) - if [ -n "$files" ]; then - while read -r file; do - log_info "找到JS文件: $file" - js_files+=("$file") - found=true - done <<< "$files" - fi - fi - done + log_warn "在标准路径模式中未找到JS文件,尝试在资源目录 '$CURSOR_RESOURCES' 中进行更广泛的搜索..." + # 查找包含特定关键字的 JS 文件 + local files=$(find "$CURSOR_RESOURCES" -name "*.js" -type f -exec grep -lE 'IOPlatformUUID|x-cursor-checksum|getMachineId' {} \; 2>/dev/null) + if [ -n "$files" ]; then + while IFS= read -r file; do + if [[ ! " ${js_files[@]} " =~ " ${file} " ]]; then + log_info "通过关键字找到可能的JS文件: $file" + js_files+=("$file") + found=true + fi + done <<< "$files" + else + log_warn "在资源目录 '$CURSOR_RESOURCES' 中通过关键字也未能找到 JS 文件。" + fi fi - + if [ "$found" = false ]; then - log_error "未找到任何可修改的JS文件" + log_error "在资源目录 '$CURSOR_RESOURCES' 中未找到任何可修改的JS文件。" + log_error "请检查 Cursor 安装是否完整,或脚本中的 JS 路径模式是否需要更新。" return 1 fi - # 保存找到的文件列表到全局变量 - CURSOR_JS_FILES=("${js_files[@]}") - log_info "找到 ${#CURSOR_JS_FILES[@]} 个JS文件需要修改" + # 去重(理论上上面的检查已经处理,但以防万一) + IFS=" " read -r -a CURSOR_JS_FILES <<< "$(echo "${js_files[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')" + + log_info "找到 ${#CURSOR_JS_FILES[@]} 个唯一的JS文件需要处理。" return 0 } @@ -444,311 +656,446 @@ modify_cursor_js_files() { # 先查找需要修改的JS文件 if ! find_cursor_js_files; then - log_error "无法找到可修改的JS文件" + # find_cursor_js_files 内部会打印错误日志 return 1 fi + if [ ${#CURSOR_JS_FILES[@]} -eq 0 ]; then + log_error "JS 文件列表为空,无法继续修改。" + return 1 + fi + local modified_count=0 - + local file_modification_status=() # 记录每个文件的修改状态 + for file in "${CURSOR_JS_FILES[@]}"; do log_info "处理文件: $file" + if [ ! -f "$file" ]; then + log_error "文件不存在: $file,跳过处理。" + file_modification_status+=("'$file': Not Found") + continue + fi + # 创建文件备份 - local backup_file="${file}.backup_$(date +%Y%m%d%H%M%S)" + local backup_file="${file}.backup_$(date +%Y%m%d_%H%M%S)" if ! cp "$file" "$backup_file"; then log_error "无法创建文件备份: $file" + file_modification_status+=("'$file': Backup Failed") continue fi - - # 确保文件可写 - chmod 644 "$file" || { - log_error "无法修改文件权限: $file" + chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$backup_file" || log_warn "设置备份文件所有权失败: $backup_file" + chmod 444 "$backup_file" || log_warn "设置备份文件权限失败: $backup_file" + + + # 确保文件对当前执行用户(root)可写 + chmod u+w "$file" || { + log_error "无法修改文件权限(写): $file" + file_modification_status+=("'$file': Permission Error (Write)") + # 尝试恢复备份(如果可能) + cp "$backup_file" "$file" 2>/dev/null || true continue } - # 检查文件内容并进行相应修改 + local modification_applied=false + + # --- 开始尝试各种修改模式 --- + + # 模式1:精确修改 x-cursor-checksum (最常见的目标之一) if grep -q 'i.header.set("x-cursor-checksum' "$file"; then - log_debug "找到 x-cursor-checksum 设置代码" - - # 执行特定的替换 - if sed -i 's/i\.header\.set("x-cursor-checksum",e===void 0?`${p}${t}`:`${p}${t}\/${e}`)/i.header.set("x-cursor-checksum",e===void 0?`${p}${t}`:`${p}${t}\/${p}`)/' "$file"; then - log_info "成功修改 x-cursor-checksum 设置代码" - ((modified_count++)) - else - log_error "修改 x-cursor-checksum 设置代码失败" - # 恢复备份 - cp "$backup_file" "$file" - fi - elif grep -q "IOPlatformUUID" "$file"; then - log_debug "找到 IOPlatformUUID 关键字" - - # 尝试不同的替换模式 - if grep -q "function a\$" "$file" && ! grep -q "return crypto.randomUUID()" "$file"; then - if sed -i 's/function a\$(t){switch/function a\$(t){return crypto.randomUUID(); switch/' "$file"; then - log_debug "成功注入 randomUUID 调用到 a\$ 函数" - ((modified_count++)) - else - log_error "修改 a\$ 函数失败" - cp "$backup_file" "$file" - fi - elif grep -q "async function v5" "$file" && ! grep -q "return crypto.randomUUID()" "$file"; then - if sed -i 's/async function v5(t){let e=/async function v5(t){return crypto.randomUUID(); let e=/' "$file"; then - log_debug "成功注入 randomUUID 调用到 v5 函数" - ((modified_count++)) + log_debug "找到 x-cursor-checksum 设置代码,尝试修改..." + # 使用更健壮的 sed,处理不同的空格和变量名可能性 + if sed -i -E 's/(i|[\w$]+)\.header\.set\("x-cursor-checksum",\s*e\s*===\s*void 0\s*\?\s*`\$\{p\}(\$\{t\})`\s*:\s*`\$\{p\}\2\/(\$\{e\})`/i.header.set("x-cursor-checksum",e===void 0?`${p}${t}`:`${p}${t}\/${p}`)/' "$file"; then + # 验证修改是否真的发生 (避免 sed 没匹配但返回0) + if ! grep -q 'i.header.set("x-cursor-checksum",e===void 0?`${p}${t}`:`${p}${t}\/${e}`)' "$file"; then + log_info "成功修改 x-cursor-checksum 设置代码" + modification_applied=true else - log_error "修改 v5 函数失败" - cp "$backup_file" "$file" + log_warn "sed 命令执行成功,但似乎未修改 x-cursor-checksum (可能模式不匹配当前版本)" fi else - # 通用注入方法 - if ! grep -q "// Cursor ID 修改工具注入" "$file"; then - local inject_code=" -// Cursor ID 修改工具注入 - $(date +%Y%m%d%H%M%S) -// 随机设备ID生成器注入 - $(date +%s) -const randomDeviceId_$(date +%s) = () => { + log_error "修改 x-cursor-checksum 设置代码失败 (sed 命令执行错误)" + fi + fi + + # 模式2:注入 randomUUID 到特定函数 (如果模式1未应用) + if [ "$modification_applied" = false ] && grep -q "IOPlatformUUID" "$file"; then + log_debug "未修改 checksum 或未找到,尝试注入 randomUUID..." + # 尝试注入 a$ 函数 + if grep -q "function a\$(" "$file" && ! grep -q "return crypto.randomUUID()" "$file"; then + if sed -i 's/function a\$(t){switch/function a\$(t){try { return require("crypto").randomUUID(); } catch(e){} switch/' "$file"; then + # 验证修改 + if grep -q "return require(\"crypto\").randomUUID()" "$file"; then + log_info "成功注入 randomUUID 调用到 a\$ 函数" + modification_applied=true + else + log_warn "sed 注入 a$ 失败(可能模式不匹配)" + fi + else + log_error "修改 a\$ 函数失败 (sed 命令执行错误)" + fi + # 尝试注入 v5 函数 (如果 a$ 没成功) + elif [ "$modification_applied" = false ] && grep -q "async function v5(" "$file" && ! grep -q "return crypto.randomUUID()" "$file"; then + if sed -i 's/async function v5(t){let e=/async function v5(t){try { return require("crypto").randomUUID(); } catch(e){} let e=/' "$file"; then + # 验证修改 + if grep -q "return require(\"crypto\").randomUUID()" "$file"; then + log_info "成功注入 randomUUID 调用到 v5 函数" + modification_applied=true + else + log_warn "sed 注入 v5 失败(可能模式不匹配)" + fi + else + log_error "修改 v5 函数失败 (sed 命令执行错误)" + fi + fi + fi + + # 模式3:通用注入 (如果上述模式都未应用,并且没有标记) + if [ "$modification_applied" = false ] && ! grep -q "// Cursor ID Modifier Injection" "$file"; then + log_debug "特定修改模式未生效或不适用,尝试通用注入..." + # 生成唯一标识符以避免冲突 + local timestamp=$(date +%s) + local new_uuid=$(generate_uuid) + local machine_id=$(generate_uuid) # 使用 UUID + local device_id=$(generate_uuid) + local mac_machine_id=$(openssl rand -hex 32) # 伪造 MAC 相关 ID + + # 创建注入代码块 + local inject_universal_code=" +// Cursor ID Modifier Injection - $timestamp +const originalRequire_$timestamp = typeof require === 'function' ? require : null; +if (originalRequire_$timestamp) { + require = function(module) { try { - return require('crypto').randomUUID(); + const result = originalRequire_$timestamp(module); + if (module === 'crypto' && result && result.randomUUID) { + const originalRandomUUID_$timestamp = result.randomUUID; + result.randomUUID = function() { return '$new_uuid'; }; + console.log('Cursor Modifier: Patched crypto.randomUUID'); + } + if (module === 'os' && result && result.networkInterfaces) { + const originalNI_$timestamp = result.networkInterfaces; + result.networkInterfaces = function() { return { lo: [{ address: '127.0.0.1', netmask: '255.0.0.0', family: 'IPv4', mac: '00:00:00:00:00:00', internal: true, cidr: '127.0.0.1/8' }]}; }; + console.log('Cursor Modifier: Patched os.networkInterfaces'); + } + return result; } catch (e) { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { - const r = Math.random() * 16 | 0; - return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); - }); + console.error('Cursor Modifier: Error in require patch for module:', module, e); + // 如果原始 require 失败,可能需要返回一个空对象或抛出异常 + // 尝试调用原始 require,即使它可能已在 try 块中失败 + try { return originalRequire_$timestamp(module); } catch (innerE) { return {}; } } -}; + }; +} else { console.warn('Cursor Modifier: Original require not found.'); } + +// Override potential global functions or properties if they exist +try { if (typeof global !== 'undefined' && global.getMachineId) global.getMachineId = function() { return '$machine_id'; }; } catch(e){} +try { if (typeof global !== 'undefined' && global.getDeviceId) global.getDeviceId = function() { return '$device_id'; }; } catch(e){} +try { if (typeof global !== 'undefined' && global.macMachineId) global.macMachineId = '$mac_machine_id'; } catch(e){} +try { if (typeof process !== 'undefined' && process.env) process.env.VSCODE_MACHINE_ID = '$machine_id'; } catch(e){} + +console.log('Cursor Modifier: Universal patches applied (UUID: $new_uuid)'); +// End Cursor ID Modifier Injection - $timestamp + " - # 将代码注入到文件开头 - echo "$inject_code" > "${file}.new" - cat "$file" >> "${file}.new" - mv "${file}.new" "$file" - - # 替换调用点 - sed -i 's/await v5(!1)/randomDeviceId_'"$(date +%s)"'()/g' "$file" - sed -i 's/a\$(t)/randomDeviceId_'"$(date +%s)"'()/g' "$file" - - log_debug "完成通用修改" - ((modified_count++)) - else - log_info "文件已经包含自定义注入代码,跳过修改" - fi + # 将变量替换进代码 + inject_universal_code=${inject_universal_code//\$new_uuid/$new_uuid} + inject_universal_code=${inject_universal_code//\$machine_id/$machine_id} + inject_universal_code=${inject_universal_code//\$device_id/$device_id} + inject_universal_code=${inject_universal_code//\$mac_machine_id/$mac_machine_id} + inject_universal_code=${inject_universal_code//\$timestamp/$timestamp} # 确保时间戳替换 + + # 将代码注入到文件开头 + local temp_inject_file=$(mktemp) + echo "$inject_universal_code" > "$temp_inject_file" + cat "$file" >> "$temp_inject_file" + + if mv "$temp_inject_file" "$file"; then + log_info "完成通用注入修改" + modification_applied=true + else + log_error "通用注入失败 (无法移动临时文件)" + rm -f "$temp_inject_file" # 清理注入文件 fi - else - # 未找到关键字,尝试通用方法 - if ! grep -q "return crypto.randomUUID()" "$file" && ! grep -q "// Cursor ID 修改工具注入" "$file"; then - # 尝试找其他关键函数 - if grep -q "function t\$()" "$file" || grep -q "async function y5" "$file"; then - # 修改 MAC 地址获取函数 - if grep -q "function t\$()" "$file"; then - sed -i 's/function t\$(){/function t\$(){return "00:00:00:00:00:00";/' "$file" - fi - - # 修改设备ID获取函数 - if grep -q "async function y5" "$file"; then - sed -i 's/async function y5(t){/async function y5(t){return crypto.randomUUID();/' "$file" - fi - - ((modified_count++)) - else - # 最通用的注入方法 - local new_uuid=$(generate_uuid) - local machine_id="auth0|user_$(openssl rand -hex 16)" - local device_id=$(generate_uuid) - local mac_machine_id=$(openssl rand -hex 32) - - local inject_universal_code=" -// Cursor ID 修改工具注入 - $(date +%Y%m%d%H%M%S) -// 全局拦截设备标识符 - $(date +%s) -const originalRequire_$(date +%s) = require; -require = function(module) { - const result = originalRequire_$(date +%s)(module); - if (module === 'crypto' && result.randomUUID) { - const originalRandomUUID_$(date +%s) = result.randomUUID; - result.randomUUID = function() { - return '$new_uuid'; - }; - } - return result; -}; + elif [ "$modification_applied" = false ]; then + log_info "文件 '$file' 似乎已被修改过 (包含注入标记),跳过通用注入。" + # 即使未应用新修改,也认为"成功"处理(避免恢复备份) + modification_applied=true # 标记为已处理,防止恢复备份 + fi -// 覆盖所有可能的系统ID获取函数 -global.getMachineId = function() { return '$machine_id'; }; -global.getDeviceId = function() { return '$device_id'; }; -global.macMachineId = '$mac_machine_id'; -" - # 替换变量 - inject_universal_code=${inject_universal_code//\$new_uuid/$new_uuid} - inject_universal_code=${inject_universal_code//\$machine_id/$machine_id} - inject_universal_code=${inject_universal_code//\$device_id/$device_id} - inject_universal_code=${inject_universal_code//\$mac_machine_id/$mac_machine_id} - - # 将代码注入到文件开头 - echo "$inject_universal_code" > "${file}.new" - cat "$file" >> "${file}.new" - mv "${file}.new" "$file" - - log_debug "完成最通用注入" - ((modified_count++)) - fi + # --- 结束修改尝试 --- + + # 根据修改结果处理 + if [ "$modification_applied" = true ]; then + ((modified_count++)) + file_modification_status+=("'$file': Success") + # 恢复文件权限为只读 + chmod u-w,go-w "$file" || log_warn "设置文件只读权限失败: $file" + # 设置文件所有者 + chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$file" || log_warn "设置 JS 文件所有权失败: $file" + else + log_error "未能成功应用任何修改到文件: $file" + file_modification_status+=("'$file': Failed") + # 恢复备份 + log_info "正在从备份恢复文件: $file" + if cp "$backup_file" "$file"; then + chmod u-w,go-w "$file" || log_warn "恢复备份后设置只读权限失败: $file" + chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$file" || log_warn "恢复备份后设置所有权失败: $file" else - log_info "文件已经被修改过,跳过修改" + log_error "从备份恢复文件失败: $file" + # 文件可能处于不确定状态 fi fi - # 恢复文件权限 - chmod 444 "$file" - done + # 清理备份文件 + rm -f "$backup_file" + + done # 文件循环结束 + # 报告每个文件的状态 + log_info "JS 文件处理状态汇总:" + for status in "${file_modification_status[@]}"; do + log_info "- $status" + done + if [ "$modified_count" -eq 0 ]; then - log_error "未能成功修改任何JS文件" + log_error "未能成功修改任何JS文件。请检查日志以获取详细信息。" return 1 fi - log_info "成功修改了 $modified_count 个JS文件" + log_info "成功修改或确认了 $modified_count 个JS文件。" return 0 } # 禁用自动更新 disable_auto_update() { - log_info "正在禁用 Cursor 自动更新..." + log_info "正在尝试禁用 Cursor 自动更新..." # 查找可能的更新配置文件 - local update_configs=( - "$CURSOR_CONFIG_DIR/update-config.json" - "$HOME/.local/share/cursor/update-config.json" - "/opt/cursor/resources/app-update.yml" - ) - - local disabled=false + local update_configs=() + # 用户配置目录下的 + if [ -d "$CURSOR_CONFIG_DIR" ]; then + update_configs+=("$CURSOR_CONFIG_DIR/update-config.json") + update_configs+=("$CURSOR_CONFIG_DIR/settings.json") # 有些设置可能在这里 + fi + # 安装目录下的 (如果资源目录确定) + if [ -n "$CURSOR_RESOURCES" ] && [ -d "$CURSOR_RESOURCES" ]; then + update_configs+=("$CURSOR_RESOURCES/resources/app-update.yml") + update_configs+=("$CURSOR_RESOURCES/app-update.yml") # 可能的位置 + fi + # 标准安装目录下的 + if [ -d "$INSTALL_DIR" ]; then + update_configs+=("$INSTALL_DIR/resources/app-update.yml") + update_configs+=("$INSTALL_DIR/app-update.yml") + fi + # $HOME/.local/share + update_configs+=("$HOME/.local/share/cursor/update-config.json") + + + local disabled_count=0 + # 处理 JSON 配置文件 + local json_config_pattern='update-config.json|settings.json' for config in "${update_configs[@]}"; do - if [ -f "$config" ]; then - log_info "找到更新配置文件: $config" - - # 备份并清空配置文件 - cp "$config" "${config}.bak" 2>/dev/null - echo '{"autoCheck": false, "autoDownload": false}' > "$config" - chmod 444 "$config" - - log_info "已禁用更新配置文件: $config" - disabled=true - fi + if [[ "$config" =~ $json_config_pattern ]] && [ -f "$config" ]; then + log_info "找到可能的更新配置文件: $config" + + # 备份 + cp "$config" "${config}.bak_$(date +%Y%m%d%H%M%S)" 2>/dev/null + + # 尝试修改 JSON (如果存在且是 settings.json) + if [[ "$config" == *settings.json ]]; then + # 尝试添加或修改 "update.mode": "none" + if grep -q '"update.mode"' "$config"; then + sed -i 's/"update.mode":[[:space:]]*"[^"]*"/"update.mode": "none"/' "$config" || log_warn "修改 settings.json 中的 update.mode 失败" + elif grep -q "}" "$config"; then # 尝试注入 + sed -i '$ s/}/,\n "update.mode": "none"\n}/' "$config" || log_warn "注入 update.mode 到 settings.json 失败" + else + log_warn "无法修改 settings.json 以禁用更新(结构未知)" + fi + # 确保权限正确 + chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$config" || log_warn "设置所有权失败: $config" + chmod 644 "$config" || log_warn "设置权限失败: $config" + ((disabled_count++)) + log_info "已尝试在 '$config' 中设置 'update.mode' 为 'none'" + elif [[ "$config" == *update-config.json ]]; then + # 直接覆盖 update-config.json + echo '{"autoCheck": false, "autoDownload": false}' > "$config" + chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$config" || log_warn "设置所有权失败: $config" + chmod 644 "$config" || log_warn "设置权限失败: $config" + ((disabled_count++)) + log_info "已覆盖更新配置文件: $config" + fi + fi done - - # 尝试查找updater可执行文件并禁用 - local updater_paths=( - "$HOME/.config/Cursor/updater" - "/opt/cursor/updater" - "/usr/lib/cursor/updater" - ) - + + # 处理 YAML 配置文件 + local yml_config_pattern='app-update.yml' + for config in "${update_configs[@]}"; do + if [[ "$config" =~ $yml_config_pattern ]] && [ -f "$config" ]; then + log_info "找到可能的更新配置文件: $config" + # 备份 + cp "$config" "${config}.bak_$(date +%Y%m%d%H%M%S)" 2>/dev/null + # 清空或修改内容 (简单起见,直接清空或写入禁用标记) + echo "# Automatic updates disabled by script $(date)" > "$config" + # echo "provider: generic" > "$config" # 或者尝试修改 provider + # echo "url: http://127.0.0.1" >> "$config" + chmod 444 "$config" # 设置为只读 + ((disabled_count++)) + log_info "已修改/清空更新配置文件: $config" + fi + done + + # 尝试查找updater可执行文件并禁用(重命名或移除权限) + local updater_paths=() + if [ -n "$CURSOR_RESOURCES" ] && [ -d "$CURSOR_RESOURCES" ]; then + updater_paths+=($(find "$CURSOR_RESOURCES" -name "updater" -type f -executable 2>/dev/null)) + updater_paths+=($(find "$CURSOR_RESOURCES" -name "CursorUpdater" -type f -executable 2>/dev/null)) # macOS 风格? + fi + if [ -d "$INSTALL_DIR" ]; then + updater_paths+=($(find "$INSTALL_DIR" -name "updater" -type f -executable 2>/dev/null)) + updater_paths+=($(find "$INSTALL_DIR" -name "CursorUpdater" -type f -executable 2>/dev/null)) + fi + updater_paths+=("$HOME/.config/Cursor/updater") # 旧位置? + for updater in "${updater_paths[@]}"; do - if [ -f "$updater" ] || [ -d "$updater" ]; then + if [ -f "$updater" ] && [ -x "$updater" ]; then log_info "找到更新程序: $updater" - if [ -f "$updater" ]; then - mv "$updater" "${updater}.bak" 2>/dev/null + local bak_updater="${updater}.bak_$(date +%Y%m%d%H%M%S)" + if mv "$updater" "$bak_updater"; then + log_info "已重命名更新程序为: $bak_updater" + ((disabled_count++)) else - touch "${updater}.disabled" + log_warn "重命名更新程序失败: $updater,尝试移除执行权限..." + if chmod a-x "$updater"; then + log_info "已移除更新程序执行权限: $updater" + ((disabled_count++)) + else + log_error "无法禁用更新程序: $updater" + fi fi - - log_info "已禁用更新程序: $updater" - disabled=true + # elif [ -d "$updater" ]; then # 如果是目录,尝试禁用 + # log_info "找到更新程序目录: $updater" + # touch "${updater}.disabled_by_script" + # log_info "已标记禁用更新程序目录: $updater" + # ((disabled_count++)) fi done - if [ "$disabled" = false ]; then - log_warn "未找到任何更新配置文件或更新程序" + if [ "$disabled_count" -eq 0 ]; then + log_warn "未能找到或禁用任何已知的自动更新机制。" + log_warn "如果 Cursor 仍然自动更新,可能需要手动查找并禁用相关文件或设置。" else - log_info "成功禁用了自动更新" + log_info "成功禁用或尝试禁用了 $disabled_count 个自动更新相关的文件/程序。" fi + return 0 # 即使没找到,也认为函数执行成功 } # 新增:通用菜单选择函数 -# 参数: -# $1 - 提示信息 -# $2 - 选项数组,格式为 "选项1|选项2|选项3" -# $3 - 默认选项索引 (从0开始) -# 返回: 选中的选项索引 (从0开始) select_menu_option() { local prompt="$1" IFS='|' read -ra options <<< "$2" local default_index=${3:-0} local selected_index=$default_index local key_input - local cursor_up='\033[A' - local cursor_down='\033[B' + local cursor_up=$'\e[A' # 更标准的 ANSI 码 + local cursor_down=$'\e[B' local enter_key=$'\n' - - # 保存光标位置 - tput sc - + + # 隐藏光标 + tput civis + # 清除可能存在的旧菜单行 (假设菜单最多 N 行) + local num_options=${#options[@]} + for ((i=0; i/dev/null || cat /etc/*release 2>/dev/null || cat /etc/issue" "系统版本信息" clear @@ -761,49 +1108,87 @@ main() { ╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ " - echo -e "${BLUE}================================${NC}" - echo -e "${GREEN} Cursor Linux 启动工具 ${NC}" - echo -e "${BLUE}================================${NC}" + echo -e "${BLUE}=====================================================${NC}" + echo -e "${GREEN} Cursor Linux 启动与修改工具 ${NC}" + echo -e "${BLUE}=====================================================${NC}" echo - echo -e "${YELLOW}[重要提示]${NC} 本工具优先修改js文件,更加安全可靠" - echo -e "${YELLOW}[重要提示]${NC} Linux 用户请注意:脚本将尝试通过检查常用路径 (${BLUE}/usr/bin, /usr/local/bin, \\$HOME/.local/bin, /opt/cursor, /snap/bin${NC}), 使用 ${BLUE}which cursor${NC}, 及搜索 ${BLUE}/usr, /opt, \\$HOME/.local${NC} 来定位 Cursor。若无法找到,请确保 Cursor 安装在上述位置之一或可通过这些方法访问。" + echo -e "${YELLOW}[提示]${NC} 本工具旨在修改 Cursor 以解决可能的启动问题或设备限制。" + echo -e "${YELLOW}[提示]${NC} 它将优先修改 JS 文件,并可选择重置设备ID和禁用自动更新。" + echo -e "${YELLOW}[提示]${NC} 如果未找到 Cursor,将尝试从 '$APPIMAGE_SEARCH_DIR' 目录安装。" echo - - # 执行主要功能 - check_permissions - - # 查找Cursor路径 - find_cursor_path - find_cursor_resources - + + # 查找 Cursor 路径 + if ! find_cursor_path; then + log_warn "系统中未找到现有的 Cursor 安装。" + select_menu_option "是否尝试从 '$APPIMAGE_SEARCH_DIR' 目录中的 AppImage 文件安装 Cursor?" "是,安装 Cursor|否,退出脚本" 0 + install_choice=$? + + if [ "$install_choice" -eq 0 ]; then + if ! install_cursor_appimage; then + log_error "Cursor 安装失败,请检查上面的日志。脚本将退出。" + exit 1 + fi + # 安装成功后,重新查找路径 + if ! find_cursor_path || ! find_cursor_resources; then + log_error "安装后仍然无法找到 Cursor 的可执行文件或资源目录。请检查 '$INSTALL_DIR' 和 '/usr/local/bin/cursor'。脚本退出。" + exit 1 + fi + log_info "Cursor 安装成功,继续执行修改步骤..." + else + log_info "用户选择不安装 Cursor,脚本退出。" + exit 0 + fi + else + # 如果找到了 Cursor,也要确保找到资源目录 + if ! find_cursor_resources; then + log_error "找到了 Cursor 可执行文件 ($CURSOR_PATH),但未能定位资源目录。" + log_error "无法继续修改 JS 文件。请检查 Cursor 安装是否完整。脚本退出。" + exit 1 + fi + log_info "发现已安装的 Cursor ($CURSOR_PATH),资源目录 ($CURSOR_RESOURCES)。" + fi + + # 到这里,Cursor 应该已安装并且路径已知 + # 检查并关闭Cursor进程 - check_and_kill_cursor - - # 备份配置文件 - backup_config + if ! check_and_kill_cursor; then + # check_and_kill_cursor 内部会记录错误并退出,但以防万一 + exit 1 + fi - # 询问用户是否需要重置机器码(默认不重置) - generate_new_config + # 备份并处理配置文件 (机器码重置选项) + if ! generate_new_config; then + log_error "处理配置文件时出错,脚本中止。" + # 此处可能需要考虑是否回滚JS修改(如果已执行)?目前不回滚。 + exit 1 + fi # 修改JS文件 - log_info "正在修改Cursor JS文件..." - if modify_cursor_js_files; then - log_info "JS文件修改成功!" + log_info "正在修改 Cursor JS 文件..." + if ! modify_cursor_js_files; then + log_error "JS 文件修改过程中发生错误。" + log_warn "配置文件可能已被修改,但 JS 文件修改失败。" + log_warn "如果重启后 Cursor 行为异常或仍有问题,请检查日志并考虑手动恢复备份或重新运行脚本。" + # 决定是否继续执行禁用更新?通常建议继续 + # exit 1 # 或者选择退出 else - log_warn "JS文件修改失败,但配置文件修改可能已成功" - log_warn "如果重启后 Cursor 仍然提示设备被禁用,请重新运行此脚本" + log_info "JS 文件修改成功!" fi # 禁用自动更新 - disable_auto_update + if ! disable_auto_update; then + # disable_auto_update 内部会记录警告,不视为致命错误 + log_warn "尝试禁用自动更新时遇到问题(详见日志),但脚本将继续。" + fi - log_info "请重启 Cursor 以应用新的配置" + log_info "所有修改步骤已完成!" + log_info "请启动 Cursor 以应用更改。" # 显示最后的提示信息 echo - echo -e "${GREEN}================================${NC}" - echo -e "${YELLOW} 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) ${NC}" - echo -e "${GREEN}================================${NC}" + echo -e "${GREEN}=====================================================${NC}" + echo -e "${YELLOW} 请关注公众号【煎饼果子卷AI】获取更多技巧和交流 ${NC}" + echo -e "${GREEN}=====================================================${NC}" echo # 记录脚本完成信息 @@ -819,3 +1204,5 @@ main() { # 执行主函数 main + +exit 0 # 确保最后返回成功状态码