功能: - 激活码管理 (Pro/Auto 两种类型) - 账号池管理 - 设备绑定记录 - 使用日志 - 搜索/筛选功能 - 禁用/启用功能 (支持退款参考) - 全局设置 (换号间隔、额度消耗等) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
281 lines
9.5 KiB
Bash
281 lines
9.5 KiB
Bash
#!/bin/bash
|
||
# ==============================================
|
||
# CursorPro - macOS 机器码重置脚本
|
||
# 一次授权,永久免密
|
||
# 纯 Shell 实现,不依赖 Python
|
||
# ==============================================
|
||
|
||
# 颜色定义
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# 路径定义
|
||
CURSOR_APP="/Applications/Cursor.app"
|
||
CURSOR_OUT="$CURSOR_APP/Contents/Resources/app/out"
|
||
MAIN_JS="$CURSOR_OUT/main.js"
|
||
UUID_PLIST="/Library/Preferences/SystemConfiguration/com.apple.platform.uuid.plist"
|
||
|
||
# 用户数据路径
|
||
USER_HOME="$HOME"
|
||
CURSOR_DATA="$USER_HOME/Library/Application Support/Cursor"
|
||
STORAGE_JSON="$CURSOR_DATA/User/globalStorage/storage.json"
|
||
STATE_VSCDB="$CURSOR_DATA/User/globalStorage/state.vscdb"
|
||
MACHINEID_FILE="$CURSOR_DATA/machineid"
|
||
|
||
# 备份目录
|
||
BACKUP_DIR="$USER_HOME/CursorPro_backups"
|
||
|
||
echo ""
|
||
echo -e "${BLUE}======================================${NC}"
|
||
echo -e "${BLUE} CursorPro macOS 机器码重置工具${NC}"
|
||
echo -e "${BLUE}======================================${NC}"
|
||
echo ""
|
||
|
||
# 检查 Cursor 是否安装
|
||
if [ ! -d "$CURSOR_APP" ]; then
|
||
echo -e "${RED}错误: 未找到 Cursor 应用${NC}"
|
||
echo "请确保 Cursor 安装在 /Applications/Cursor.app"
|
||
exit 1
|
||
fi
|
||
|
||
# 创建备份目录
|
||
mkdir -p "$BACKUP_DIR" 2>/dev/null
|
||
|
||
# ============================================
|
||
# 第一步:检测并设置权限(一次性)
|
||
# ============================================
|
||
echo -e "${YELLOW}[步骤 1/7] 检查权限...${NC}"
|
||
|
||
NEED_SUDO=false
|
||
|
||
# 检查 main.js 权限
|
||
if [ ! -w "$MAIN_JS" ] 2>/dev/null; then
|
||
NEED_SUDO=true
|
||
echo " - main.js: 需要授权"
|
||
else
|
||
echo -e " - main.js: ${GREEN}已有权限${NC}"
|
||
fi
|
||
|
||
# 检查 UUID plist 权限(文件可能不存在)
|
||
if [ -f "$UUID_PLIST" ]; then
|
||
if [ ! -w "$UUID_PLIST" ] 2>/dev/null; then
|
||
NEED_SUDO=true
|
||
echo " - UUID plist: 需要授权"
|
||
else
|
||
echo -e " - UUID plist: ${GREEN}已有权限${NC}"
|
||
fi
|
||
else
|
||
echo -e " - UUID plist: ${YELLOW}文件不存在,跳过${NC}"
|
||
fi
|
||
|
||
# 如果需要授权
|
||
if [ "$NEED_SUDO" = true ]; then
|
||
echo ""
|
||
echo -e "${YELLOW}首次运行,需要管理员权限${NC}"
|
||
echo -e "${YELLOW}授权后以后重置不再需要输入密码${NC}"
|
||
echo ""
|
||
|
||
# 请求 sudo 权限
|
||
sudo -v || { echo -e "${RED}授权失败${NC}"; exit 1; }
|
||
|
||
# 修改 Cursor 目录权限
|
||
if [ ! -w "$MAIN_JS" ] 2>/dev/null; then
|
||
echo " 正在修改 Cursor 目录权限..."
|
||
sudo chown -R $(whoami) "$CURSOR_OUT" 2>/dev/null || true
|
||
sudo chmod -R u+rw "$CURSOR_OUT" 2>/dev/null || true
|
||
echo -e " ${GREEN}✓ Cursor 目录权限已修改${NC}"
|
||
fi
|
||
|
||
# 修改 UUID plist 权限(如果文件存在)
|
||
if [ -f "$UUID_PLIST" ] && [ ! -w "$UUID_PLIST" ] 2>/dev/null; then
|
||
echo " 正在修改 UUID plist 权限..."
|
||
sudo chown $(whoami) "$UUID_PLIST" 2>/dev/null || true
|
||
echo -e " ${GREEN}✓ UUID plist 权限已修改${NC}"
|
||
fi
|
||
|
||
echo ""
|
||
echo -e "${GREEN}✓ 权限设置完成!以后重置不再需要密码${NC}"
|
||
fi
|
||
|
||
echo ""
|
||
|
||
# ============================================
|
||
# 第二步:关闭 Cursor
|
||
# ============================================
|
||
echo -e "${YELLOW}[步骤 2/7] 关闭 Cursor...${NC}"
|
||
|
||
if pgrep -x "Cursor" > /dev/null; then
|
||
killall Cursor 2>/dev/null || true
|
||
echo " 等待 Cursor 完全退出..."
|
||
sleep 3
|
||
echo -e " ${GREEN}✓ Cursor 已关闭${NC}"
|
||
else
|
||
echo -e " ${GREEN}✓ Cursor 未运行${NC}"
|
||
fi
|
||
|
||
echo ""
|
||
|
||
# ============================================
|
||
# 第三步:Patch main.js
|
||
# ============================================
|
||
echo -e "${YELLOW}[步骤 3/7] Patch main.js...${NC}"
|
||
|
||
if [ ! -f "$MAIN_JS" ]; then
|
||
echo -e " ${RED}警告: 未找到 main.js${NC}"
|
||
else
|
||
# 检查是否已经 patch 过
|
||
if grep -q 'uuidgen' "$MAIN_JS" 2>/dev/null; then
|
||
echo -e " ${GREEN}✓ main.js 已经 Patch 过,跳过${NC}"
|
||
else
|
||
# 检查目标字符串
|
||
if grep -q 'ioreg -rd1 -c IOPlatformExpertDevice' "$MAIN_JS"; then
|
||
# 备份到用户目录
|
||
BACKUP_FILE="$BACKUP_DIR/main.js.backup_$(date +%s)"
|
||
cp "$MAIN_JS" "$BACKUP_FILE" 2>/dev/null
|
||
if [ $? -eq 0 ]; then
|
||
echo " 备份已创建: $BACKUP_FILE"
|
||
fi
|
||
|
||
# 使用 perl 替换(macOS 自带,比 sed 更可靠处理特殊字符)
|
||
perl -i -pe 's/ioreg -rd1 -c IOPlatformExpertDevice/UUID=\$(uuidgen | tr '"'"'[:upper:]'"'"' '"'"'[:lower:]'"'"');echo "IOPlatformUUID = "\$UUID";/g' "$MAIN_JS" 2>/dev/null
|
||
|
||
if [ $? -eq 0 ]; then
|
||
echo -e " ${GREEN}✓ main.js Patch 成功${NC}"
|
||
else
|
||
echo -e " ${RED}✗ main.js Patch 失败${NC}"
|
||
fi
|
||
else
|
||
echo -e " ${YELLOW}警告: 未找到目标字符串,可能已 patch 或版本不兼容${NC}"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
echo ""
|
||
|
||
# ============================================
|
||
# 第四步:重置系统 UUID
|
||
# ============================================
|
||
echo -e "${YELLOW}[步骤 4/7] 重置系统 UUID...${NC}"
|
||
|
||
if [ ! -f "$UUID_PLIST" ]; then
|
||
echo -e " ${YELLOW}提示: UUID plist 文件不存在,跳过${NC}"
|
||
else
|
||
NEW_SYS_UUID=$(uuidgen | tr '[:upper:]' '[:lower:]')
|
||
/usr/libexec/PlistBuddy -c "Set :IOPlatformUUID $NEW_SYS_UUID" "$UUID_PLIST" 2>/dev/null || \
|
||
/usr/libexec/PlistBuddy -c "Add :IOPlatformUUID string $NEW_SYS_UUID" "$UUID_PLIST" 2>/dev/null || true
|
||
echo -e " ${GREEN}✓ 系统 UUID 已重置: $NEW_SYS_UUID${NC}"
|
||
fi
|
||
|
||
echo ""
|
||
|
||
# ============================================
|
||
# 第五步:重置 storage.json
|
||
# ============================================
|
||
echo -e "${YELLOW}[步骤 5/7] 重置 storage.json...${NC}"
|
||
|
||
# 生成新的机器码(64位十六进制)
|
||
NEW_MACHINE_ID=$(uuidgen | tr -d '-' | tr '[:upper:]' '[:lower:]')$(uuidgen | tr -d '-' | tr '[:upper:]' '[:lower:]')
|
||
NEW_MAC_MACHINE_ID=$(uuidgen | tr -d '-' | tr '[:upper:]' '[:lower:]')$(uuidgen | tr -d '-' | tr '[:upper:]' '[:lower:]')
|
||
NEW_DEV_DEVICE_ID=$(uuidgen | tr '[:upper:]' '[:lower:]')
|
||
NEW_SQM_ID="{$(uuidgen | tr '[:lower:]' '[:upper:]')}"
|
||
|
||
if [ -f "$STORAGE_JSON" ]; then
|
||
# 备份
|
||
cp "$STORAGE_JSON" "$BACKUP_DIR/storage.json.backup_$(date +%s)" 2>/dev/null
|
||
|
||
# 使用 sed 替换 JSON 中的值(macOS sed 语法)
|
||
# machineId
|
||
sed -i '' "s/\"telemetry\.machineId\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"telemetry.machineId\": \"$NEW_MACHINE_ID\"/g" "$STORAGE_JSON" 2>/dev/null
|
||
|
||
# macMachineId
|
||
sed -i '' "s/\"telemetry\.macMachineId\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"telemetry.macMachineId\": \"$NEW_MAC_MACHINE_ID\"/g" "$STORAGE_JSON" 2>/dev/null
|
||
|
||
# devDeviceId
|
||
sed -i '' "s/\"telemetry\.devDeviceId\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"telemetry.devDeviceId\": \"$NEW_DEV_DEVICE_ID\"/g" "$STORAGE_JSON" 2>/dev/null
|
||
|
||
# sqmId
|
||
sed -i '' "s/\"telemetry\.sqmId\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"telemetry.sqmId\": \"$NEW_SQM_ID\"/g" "$STORAGE_JSON" 2>/dev/null
|
||
|
||
echo -e " ${GREEN}✓ storage.json 已更新 (4个ID)${NC}"
|
||
else
|
||
echo -e " ${YELLOW}警告: 未找到 storage.json${NC}"
|
||
fi
|
||
|
||
echo ""
|
||
|
||
# ============================================
|
||
# 第六步:重置 SQLite 数据库
|
||
# ============================================
|
||
echo -e "${YELLOW}[步骤 6/7] 重置 SQLite 数据库...${NC}"
|
||
|
||
NEW_SERVICE_MACHINE_ID=$(uuidgen | tr '[:upper:]' '[:lower:]')
|
||
|
||
if [ -f "$STATE_VSCDB" ]; then
|
||
# 备份
|
||
cp "$STATE_VSCDB" "$BACKUP_DIR/state.vscdb.backup_$(date +%s)" 2>/dev/null
|
||
|
||
# 使用 sqlite3 命令(macOS 自带)
|
||
sqlite3 "$STATE_VSCDB" "UPDATE ItemTable SET value = '$NEW_SERVICE_MACHINE_ID' WHERE key = 'storage.serviceMachineId';" 2>/dev/null
|
||
|
||
if [ $? -eq 0 ]; then
|
||
echo -e " ${GREEN}✓ state.vscdb 已更新 (serviceMachineId)${NC}"
|
||
else
|
||
# 如果更新失败,尝试插入
|
||
sqlite3 "$STATE_VSCDB" "INSERT OR REPLACE INTO ItemTable (key, value) VALUES ('storage.serviceMachineId', '$NEW_SERVICE_MACHINE_ID');" 2>/dev/null
|
||
echo -e " ${GREEN}✓ state.vscdb 已更新${NC}"
|
||
fi
|
||
else
|
||
echo -e " ${YELLOW}警告: 未找到 state.vscdb${NC}"
|
||
fi
|
||
|
||
# 更新 machineid 文件
|
||
echo ""
|
||
if [ -d "$(dirname "$MACHINEID_FILE")" ]; then
|
||
echo "${NEW_MACHINE_ID:0:64}" > "$MACHINEID_FILE"
|
||
echo -e " ${GREEN}✓ machineid 文件已更新${NC}"
|
||
else
|
||
echo -e " ${YELLOW}警告: 未找到 machineid 目录${NC}"
|
||
fi
|
||
|
||
# 清理缓存
|
||
rm -rf "$CURSOR_DATA/Cache" 2>/dev/null || true
|
||
rm -rf "$CURSOR_DATA/CachedData" 2>/dev/null || true
|
||
rm -rf "$CURSOR_DATA/GPUCache" 2>/dev/null || true
|
||
echo -e " ${GREEN}✓ 缓存已清理${NC}"
|
||
|
||
echo ""
|
||
|
||
# ============================================
|
||
# 第七步:重新打开 Cursor
|
||
# ============================================
|
||
echo -e "${YELLOW}[步骤 7/7] 重新打开 Cursor...${NC}"
|
||
|
||
sleep 1
|
||
open "$CURSOR_APP"
|
||
echo -e " ${GREEN}✓ Cursor 已启动${NC}"
|
||
|
||
echo ""
|
||
echo -e "${GREEN}======================================${NC}"
|
||
echo -e "${GREEN} ✅ 机器码重置完成!${NC}"
|
||
echo -e "${GREEN}======================================${NC}"
|
||
echo ""
|
||
echo "已重置的内容:"
|
||
echo " ✓ main.js (ioreg patch)"
|
||
echo " ✓ storage.json (4个ID)"
|
||
echo " ✓ state.vscdb (serviceMachineId)"
|
||
echo " ✓ machineid 文件"
|
||
echo " ✓ 缓存已清理"
|
||
echo ""
|
||
echo "新机器码信息:"
|
||
echo " machineId: ${NEW_MACHINE_ID:0:32}..."
|
||
echo " devDeviceId: $NEW_DEV_DEVICE_ID"
|
||
echo " serviceMachineId: $NEW_SERVICE_MACHINE_ID"
|
||
echo ""
|
||
echo "备份位置: $BACKUP_DIR"
|
||
echo ""
|
||
echo -e "${BLUE}此窗口可以关闭${NC}"
|
||
echo ""
|