go-cursor-help/scripts/run/cursor_linux_id_modifier.sh

821 lines
26 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# 设置错误处理
set -e
# 定义日志文件路径
LOG_FILE="/tmp/cursor_linux_id_modifier.log"
# 初始化日志文件
initialize_log() {
echo "========== Cursor ID 修改工具日志开始 $(date) ==========" > "$LOG_FILE"
chmod 644 "$LOG_FILE"
}
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 日志函数 - 同时输出到终端和日志文件
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
echo "[WARN] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE"
}
log_debug() {
echo -e "${BLUE}[DEBUG]${NC} $1"
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE"
}
# 记录命令输出到日志文件
log_cmd_output() {
local cmd="$1"
local msg="$2"
echo "[CMD] $(date '+%Y-%m-%d %H:%M:%S') 执行命令: $cmd" >> "$LOG_FILE"
echo "[CMD] $msg:" >> "$LOG_FILE"
eval "$cmd" 2>&1 | tee -a "$LOG_FILE"
echo "" >> "$LOG_FILE"
}
# 获取当前用户
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
# 定义Linux下的Cursor路径
CURSOR_CONFIG_DIR="$HOME/.config/Cursor"
STORAGE_FILE="$CURSOR_CONFIG_DIR/User/globalStorage/storage.json"
BACKUP_DIR="$CURSOR_CONFIG_DIR/User/globalStorage/backups"
# 可能的Cursor二进制路径
CURSOR_BIN_PATHS=(
"/usr/bin/cursor"
"/usr/local/bin/cursor"
"$HOME/.local/bin/cursor"
"/opt/cursor/cursor"
"/snap/bin/cursor"
)
# 找到Cursor安装路径
find_cursor_path() {
log_info "查找Cursor安装路径..."
for path in "${CURSOR_BIN_PATHS[@]}"; do
if [ -f "$path" ]; then
log_info "找到Cursor安装路径: $path"
CURSOR_PATH="$path"
return 0
fi
done
# 尝试通过which命令定位
if command -v cursor &> /dev/null; then
CURSOR_PATH=$(which cursor)
log_info "通过which找到Cursor: $CURSOR_PATH"
return 0
fi
# 尝试查找可能的安装路径
local cursor_paths=$(find /usr /opt $HOME/.local -name "cursor" -type f -executable 2>/dev/null)
if [ -n "$cursor_paths" ]; then
CURSOR_PATH=$(echo "$cursor_paths" | head -1)
log_info "通过查找找到Cursor: $CURSOR_PATH"
return 0
fi
log_warn "未找到Cursor可执行文件将尝试使用配置目录"
return 1
}
# 查找并定位Cursor资源文件目录
find_cursor_resources() {
log_info "查找Cursor资源目录..."
# 可能的资源目录路径
local resource_paths=(
"/usr/lib/cursor"
"/usr/share/cursor"
"/opt/cursor"
"$HOME/.local/share/cursor"
)
for path in "${resource_paths[@]}"; do
if [ -d "$path" ]; then
log_info "找到Cursor资源目录: $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"
log_info "通过二进制路径找到资源目录: $CURSOR_RESOURCES"
return 0
fi
fi
log_warn "未找到Cursor资源目录"
return 1
}
# 检查权限
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 "cursor" | grep -v grep
}
while [ $attempt -le $max_attempts ]; do
# 使用更精确的匹配来获取 Cursor 进程
CURSOR_PIDS=$(pgrep -f "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 -f "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 "请手动关闭进程后重试"
exit 1
}
# 备份配置文件
backup_config() {
if [ ! -f "$STORAGE_FILE" ]; then
log_warn "配置文件不存在,跳过备份"
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
}
# 生成随机 ID
generate_random_id() {
# 生成32字节(64个十六进制字符)的随机数
openssl rand -hex 32
}
# 生成随机 UUID
generate_uuid() {
# 在Linux上使用uuidgen生成UUID
if command -v uuidgen &> /dev/null; then
uuidgen | tr '[:upper:]' '[:lower:]'
else
# 备选方案:使用/proc/sys/kernel/random/uuid
if [ -f /proc/sys/kernel/random/uuid ]; then
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/'
fi
fi
}
# 修改现有文件
modify_or_add_config() {
local key="$1"
local value="$2"
local file="$3"
if [ ! -f "$file" ]; then
log_error "文件不存在: $file"
return 1
fi
# 确保文件可写
chmod 644 "$file" || {
log_error "无法修改文件权限: $file"
return 1
}
# 创建临时文件
local temp_file=$(mktemp)
# 检查key是否存在
if grep -q "\"$key\":" "$file"; then
# key存在,执行替换
sed "s/\"$key\":[[:space:]]*\"[^\"]*\"/\"$key\": \"$value\"/" "$file" > "$temp_file" || {
log_error "修改配置失败: $key"
rm -f "$temp_file"
return 1
}
else
# key不存在,添加新的key-value对
sed "s/}$/,\n \"$key\": \"$value\"\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"
return 0
}
# 生成新的配置
generate_new_config() {
echo
log_warn "机器码重置选项"
# 使用菜单选择函数询问用户是否重置机器码
select_menu_option "是否需要重置机器码? (通常情况下只修改js文件即可)" "不重置 - 仅修改js文件即可|重置 - 同时修改配置文件和机器码" 0
reset_choice=$?
# 记录日志以便调试
echo "[INPUT_DEBUG] 机器码重置选项选择: $reset_choice" >> "$LOG_FILE"
# 处理用户选择 - 索引0对应"不重置"选项索引1对应"重置"选项
if [ "$reset_choice" = "1" ]; then
log_info "您选择了重置机器码"
# 确保配置文件目录存在
if [ -f "$STORAGE_FILE" ]; then
log_info "发现已有配置文件: $STORAGE_FILE"
# 备份现有配置(以防万一)
backup_config
# 生成并设置新的设备ID
local new_device_id=$(generate_uuid)
local new_machine_id="auth0|user_$(openssl rand -hex 16)"
log_info "正在设置新的设备和机器ID..."
log_debug "新设备ID: $new_device_id"
log_debug "新机器ID: $new_machine_id"
# 修改配置文件
if modify_or_add_config "deviceId" "$new_device_id" "$STORAGE_FILE" && \
modify_or_add_config "machineId" "$new_machine_id" "$STORAGE_FILE"; then
log_info "配置文件修改成功"
else
log_error "配置文件修改失败"
fi
else
log_warn "未找到配置文件这是正常的脚本将跳过ID修改"
fi
else
log_info "您选择了不重置机器码将仅修改js文件"
# 确保配置文件目录存在
if [ -f "$STORAGE_FILE" ]; then
log_info "发现已有配置文件: $STORAGE_FILE"
# 备份现有配置(以防万一)
backup_config
else
log_warn "未找到配置文件这是正常的脚本将跳过ID修改"
fi
fi
echo
log_info "配置处理完成"
}
# 查找Cursor的JS文件
find_cursor_js_files() {
log_info "查找Cursor的JS文件..."
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
log_info "找到JS文件: $file"
js_files+=("$file")
found=true
done <<< "$files"
fi
done
fi
# 如果还没找到,尝试在/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
fi
if [ "$found" = false ]; then
log_error "未找到任何可修改的JS文件"
return 1
fi
# 保存找到的文件列表到全局变量
CURSOR_JS_FILES=("${js_files[@]}")
log_info "找到 ${#CURSOR_JS_FILES[@]} 个JS文件需要修改"
return 0
}
# 修改Cursor的JS文件
modify_cursor_js_files() {
log_info "开始修改Cursor的JS文件..."
# 先查找需要修改的JS文件
if ! find_cursor_js_files; then
log_error "无法找到可修改的JS文件"
return 1
fi
local modified_count=0
for file in "${CURSOR_JS_FILES[@]}"; do
log_info "处理文件: $file"
# 创建文件备份
local backup_file="${file}.backup_$(date +%Y%m%d%H%M%S)"
if ! cp "$file" "$backup_file"; then
log_error "无法创建文件备份: $file"
continue
fi
# 确保文件可写
chmod 644 "$file" || {
log_error "无法修改文件权限: $file"
continue
}
# 检查文件内容并进行相应修改
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++))
else
log_error "修改 v5 函数失败"
cp "$backup_file" "$file"
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) = () => {
try {
return require('crypto').randomUUID();
} 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);
});
}
};
"
# 将代码注入到文件开头
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
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;
};
// 覆盖所有可能的系统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
else
log_info "文件已经被修改过,跳过修改"
fi
fi
# 恢复文件权限
chmod 444 "$file"
done
if [ "$modified_count" -eq 0 ]; then
log_error "未能成功修改任何JS文件"
return 1
fi
log_info "成功修改了 $modified_count 个JS文件"
return 0
}
# 禁用自动更新
disable_auto_update() {
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
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
done
# 尝试查找updater可执行文件并禁用
local updater_paths=(
"$HOME/.config/Cursor/updater"
"/opt/cursor/updater"
"/usr/lib/cursor/updater"
)
for updater in "${updater_paths[@]}"; do
if [ -f "$updater" ] || [ -d "$updater" ]; then
log_info "找到更新程序: $updater"
if [ -f "$updater" ]; then
mv "$updater" "${updater}.bak" 2>/dev/null
else
touch "${updater}.disabled"
fi
log_info "已禁用更新程序: $updater"
disabled=true
fi
done
if [ "$disabled" = false ]; then
log_warn "未找到任何更新配置文件或更新程序"
else
log_info "成功禁用了自动更新"
fi
}
# 新增:通用菜单选择函数
# 参数:
# $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 enter_key=$'\n'
# 保存光标位置
tput sc
# 显示提示信息
echo -e "$prompt"
# 第一次显示菜单
for i in "${!options[@]}"; do
if [ $i -eq $selected_index ]; then
echo -e " ${GREEN}${NC} ${options[$i]}"
else
echo -e " ${options[$i]}"
fi
done
# 循环处理键盘输入
while true; do
# 读取单个按键
read -rsn3 key_input
# 检测按键
case "$key_input" in
# 上箭头键
$'\033[A')
if [ $selected_index -gt 0 ]; then
((selected_index--))
fi
;;
# 下箭头键
$'\033[B')
if [ $selected_index -lt $((${#options[@]}-1)) ]; then
((selected_index++))
fi
;;
# Enter键
"")
echo # 换行
log_info "您选择了: ${options[$selected_index]}"
return $selected_index
;;
esac
# 恢复光标位置
tput rc
# 重新显示菜单
for i in "${!options[@]}"; do
if [ $i -eq $selected_index ]; then
echo -e " ${GREEN}${NC} ${options[$i]}"
else
echo -e " ${options[$i]}"
fi
done
done
}
# 主函数
main() {
# 检查系统环境
if [[ $(uname) != "Linux" ]]; then
log_error "本脚本仅支持 Linux 系统"
exit 1
fi
# 初始化日志文件
initialize_log
log_info "脚本启动..."
# 记录系统信息
log_info "系统信息: $(uname -a)"
log_info "当前用户: $CURRENT_USER"
log_cmd_output "lsb_release -a 2>/dev/null || cat /etc/*release 2>/dev/null || cat /etc/issue" "系统版本信息"
clear
# 显示 Logo
echo -e "
██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗
██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗
██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝
██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗
╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║
╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝
"
echo -e "${BLUE}================================${NC}"
echo -e "${GREEN} Cursor Linux 启动工具 ${NC}"
echo -e "${BLUE}================================${NC}"
echo
echo -e "${YELLOW}[重要提示]${NC} 本工具优先修改js文件更加安全可靠"
echo
# 执行主要功能
check_permissions
# 查找Cursor路径
find_cursor_path
find_cursor_resources
# 检查并关闭Cursor进程
check_and_kill_cursor
# 备份配置文件
backup_config
# 询问用户是否需要重置机器码(默认不重置)
generate_new_config
# 修改JS文件
log_info "正在修改Cursor JS文件..."
if modify_cursor_js_files; then
log_info "JS文件修改成功"
else
log_warn "JS文件修改失败但配置文件修改可能已成功"
log_warn "如果重启后 Cursor 仍然提示设备被禁用,请重新运行此脚本"
fi
# 禁用自动更新
disable_auto_update
log_info "请重启 Cursor 以应用新的配置"
# 显示最后的提示信息
echo
echo -e "${GREEN}================================${NC}"
echo -e "${YELLOW} 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) ${NC}"
echo -e "${GREEN}================================${NC}"
echo
# 记录脚本完成信息
log_info "脚本执行完成"
echo "========== Cursor ID 修改工具日志结束 $(date) ==========" >> "$LOG_FILE"
# 显示日志文件位置
echo
log_info "详细日志已保存到: $LOG_FILE"
echo "如遇问题请将此日志文件提供给开发者以协助排查"
echo
}
# 执行主函数
main