feat: 全面优化用户界面和操作流程 1.统一所有弹窗界面风格 2.优化禁用更新/突破限制/刷新授权的操作流程 3.增加复制命令按钮功能 4.改进错误提示和操作指引 5.提升整体操作流畅度

This commit is contained in:
huangzhenpc
2025-02-14 20:44:16 +08:00
parent 26e159d71c
commit ae698796e7
9 changed files with 945 additions and 509 deletions

View File

@@ -352,106 +352,30 @@ class AccountSwitcher:
return "网络请求失败,请稍后重试"
return str(error)
def get_process_details(self, process_name: str) -> List[Dict]:
"""获取进程详细信息
Args:
process_name: 进程名称
Returns:
List[Dict]: 进程详细信息列表
"""
try:
# 使用 tasklist 命令替代 wmi
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
output = subprocess.check_output(
f'tasklist /FI "IMAGENAME eq {process_name}" /FO CSV /NH',
startupinfo=startupinfo,
shell=True
).decode('gbk')
processes = []
if output.strip():
for line in output.strip().split('\n'):
if line.strip():
parts = line.strip('"').split('","')
if len(parts) >= 2:
processes.append({
'name': parts[0],
'pid': parts[1]
})
return processes
except Exception as e:
logging.error(f"获取进程信息失败: {str(e)}")
return []
def close_cursor_process(self) -> bool:
"""关闭所有Cursor进程
Returns:
bool: 是否成功关闭所有进程
"""
"""关闭所有Cursor相关进程"""
try:
if sys.platform == "win32":
# 创建startupinfo对象来隐藏命令行窗口
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
# 获取进程详情
processes = self.get_process_details("Cursor.exe")
if processes:
logging.info(f"发现 {len(processes)} 个Cursor进程")
for p in processes:
logging.info(f"进程信息: PID={p['pid']}, 路径={p['name']}")
# 直接使用taskkill命令强制结束所有相关进程
process_names = ["Cursor.exe", "devsense.php.ls.exe", "intelliphp.ls.exe"]
for name in process_names:
try:
subprocess.run(
f"taskkill /f /t /im {name} >nul 2>&1",
startupinfo=startupinfo,
shell=True,
timeout=5
)
except:
pass
# 尝试关闭进程
subprocess.run(
"taskkill /f /im Cursor.exe >nul 2>&1",
startupinfo=startupinfo,
shell=True
)
# 等待进程关闭,增加重试次数和等待时间
retry_count = 0
max_retries = 10 # 增加最大重试次数
wait_time = 2 # 增加每次等待时间
while retry_count < max_retries:
remaining_processes = self.get_process_details("Cursor.exe")
if not remaining_processes:
logging.info("所有Cursor进程已关闭")
# 额外等待一段时间确保系统资源完全释放
time.sleep(2)
return True
retry_count += 1
if retry_count >= max_retries:
processes = self.get_process_details("Cursor.exe")
if processes:
logging.error(f"无法关闭以下进程:")
for p in processes:
logging.error(f"PID={p['pid']}, 路径={p['name']}")
# 最后一次尝试强制结束
try:
subprocess.run(
"taskkill /f /im Cursor.exe /t >nul 2>&1",
startupinfo=startupinfo,
shell=True
)
time.sleep(2)
except:
pass
return False
logging.warning(f"等待进程关闭, 尝试 {retry_count}/{max_retries}...")
time.sleep(wait_time)
# 等待一小段时间确保进程完全关闭
time.sleep(2)
return True
else:
# 其他系统的处理
if sys.platform == "darwin":
subprocess.run("killall Cursor 2>/dev/null", shell=True)
else:
@@ -472,12 +396,11 @@ class AccountSwitcher:
return False
# 2. 使用新的重置工具类执行重置
success, message = self.resetter.reset_cursor(disable_update=True)
success, message = self.resetter.reset_cursor(disable_update=False) # 不在这里禁用更新
if not success:
logging.error(f"重置失败: {message}")
return False
# 不在这里重启Cursor让调用者决定何时重启
logging.info("机器码重置完成")
return True
@@ -486,104 +409,55 @@ class AccountSwitcher:
return False
def restart_cursor(self) -> bool:
"""重启Cursor编辑器
Returns:
bool: 是否成功重启
"""
"""重启Cursor编辑器"""
try:
logging.info("正在重启Cursor...")
# 1. 确保进程已关闭
self.close_cursor_process()
# 确保进程已关闭
if not self.close_cursor_process():
logging.error("无法关闭Cursor进程")
return False
# 等待系统资源释放
time.sleep(3)
# 2. 等待一小段时间
time.sleep(2)
# 启动Cursor
# 3. 启动Cursor
if sys.platform == "win32":
cursor_exe = self.cursor_path / "Cursor.exe"
if cursor_exe.exists():
max_retries = 3
for attempt in range(max_retries):
try:
# 使用subprocess启动
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
try:
# 使用ShellExecuteW以管理员权限启动
result = ctypes.windll.shell32.ShellExecuteW(
None, # 父窗口句柄
"runas", # 以管理员权限运行
str(cursor_exe), # 程序路径
None, # 参数
str(cursor_exe.parent), # 工作目录
1 # 正常窗口显示
)
# ShellExecuteW返回值大于32表示成功
if result > 32:
time.sleep(3) # 等待启动
return True
else:
logging.error(f"启动Cursor失败错误码: {result}")
return False
subprocess.Popen(
str(cursor_exe),
startupinfo=startupinfo,
creationflags=subprocess.CREATE_NEW_CONSOLE
)
# 等待进程启动
time.sleep(5) # 增加等待时间
# 验证进程是否启动
processes = self.get_process_details("Cursor.exe")
if processes:
logging.info("Cursor启动成功")
return True
else:
if attempt < max_retries - 1:
logging.warning(f"启动尝试 {attempt + 1} 失败,准备重试...")
time.sleep(3)
continue
logging.error("Cursor进程未找到")
# 尝试使用 os.startfile 作为备选方案
try:
os.startfile(str(cursor_exe))
time.sleep(5)
logging.info("使用备选方案启动Cursor")
return True
except Exception as e:
logging.error(f"备选启动方案失败: {str(e)}")
return False
except Exception as e:
if attempt < max_retries - 1:
logging.warning(f"启动尝试 {attempt + 1} 失败: {str(e)},准备重试...")
time.sleep(3)
continue
logging.error(f"启动Cursor失败: {str(e)}")
# 尝试使用 os.startfile 作为最后的备选方案
try:
os.startfile(str(cursor_exe))
time.sleep(5)
logging.info("使用备选方案启动Cursor")
return True
except Exception as e:
logging.error(f"备选启动方案失败: {str(e)}")
return False
except Exception as e:
logging.error(f"启动Cursor失败: {str(e)}")
return False
else:
logging.error(f"未找到Cursor程序: {cursor_exe}")
return False
elif sys.platform == "darwin":
else:
try:
subprocess.run("open -a Cursor", shell=True, check=True)
time.sleep(5)
logging.info("Cursor启动成功")
if sys.platform == "darwin":
subprocess.run("open -a Cursor", shell=True, check=True)
else:
subprocess.run("sudo cursor &", shell=True, check=True)
time.sleep(3)
return True
except subprocess.CalledProcessError as e:
logging.error(f"启动Cursor失败: {str(e)}")
return False
elif sys.platform == "linux":
try:
subprocess.run("cursor &", shell=True, check=True)
time.sleep(5)
logging.info("Cursor启动成功")
return True
except subprocess.CalledProcessError as e:
except Exception as e:
logging.error(f"启动Cursor失败: {str(e)}")
return False
return False
except Exception as e:
logging.error(f"重启Cursor失败: {str(e)}")
return False
@@ -776,12 +650,14 @@ class AccountSwitcher:
if not self.reset_machine_id():
return False, "重置机器码失败"
# 7. 重启Cursor(只在这里执行一次重启)
# 7. 重启Cursor
logging.info("正在重启Cursor...")
retry_count = 0
max_retries = 3
while retry_count < max_retries:
if self.restart_cursor():
# 等待进程完全启动
time.sleep(5)
break
retry_count += 1
if retry_count < max_retries:
@@ -824,50 +700,17 @@ class AccountSwitcher:
"""禁用Cursor更新"""
try:
# 1. 先关闭所有Cursor进程
if sys.platform == "win32":
# 创建startupinfo对象来隐藏命令行窗口
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
# 关闭Cursor
subprocess.run(
"taskkill /f /im Cursor.exe >nul 2>&1",
startupinfo=startupinfo,
shell=True
)
time.sleep(2)
if not self.close_cursor_process():
return False, "无法关闭Cursor进程请手动关闭后重试"
# 2. 删除updater目录并创建同名文件以阻止更新
updater_path = Path(os.getenv('LOCALAPPDATA')) / "cursor-updater"
# 2. 调用resetter的disable_auto_update方法
success, message = self.resetter.disable_auto_update()
if not success:
logging.error(f"禁用自动更新失败: {message}")
return False, f"禁用更新失败: {message}"
try:
# 如果是目录,则删除
if updater_path.is_dir():
import shutil
shutil.rmtree(str(updater_path))
logging.info("删除updater目录成功")
# 如果是文件,则删除
if updater_path.is_file():
os.remove(str(updater_path))
logging.info("删除updater文件成功")
# 创建同名空文件
updater_path.touch()
logging.info("创建updater空文件成功")
# 3. 重启Cursor
cursor_exe = self.cursor_path / "Cursor.exe"
if cursor_exe.exists():
os.startfile(str(cursor_exe))
logging.info("Cursor重启成功")
return True, "Cursor更新已禁用程序已重启"
except Exception as e:
logging.error(f"操作updater文件失败: {str(e)}")
return False, f"禁用更新失败: {str(e)}"
logging.info("更新已禁用")
return True, "Cursor更新已禁用"
except Exception as e:
logging.error(f"禁用Cursor更新失败: {str(e)}")
@@ -967,6 +810,26 @@ class AccountSwitcher:
return False, "多次尝试后心跳发送失败"
def bypass_cursor_limit(self) -> Tuple[bool, str]:
"""突破Cursor使用限制"""
try:
# 1. 先关闭所有Cursor进程
if not self.close_cursor_process():
return False, "无法关闭Cursor进程请手动关闭后重试"
# 2. 调用resetter的reset_cursor方法
success, message = self.resetter.reset_cursor(disable_update=True)
if not success:
logging.error(f"突破限制失败: {message}")
return False, f"突破限制失败: {message}"
logging.info("突破限制成功")
return True, "突破限制成功"
except Exception as e:
logging.error(f"突破限制失败: {str(e)}")
return False, f"突破限制失败: {str(e)}"
def main():
"""主函数"""
try:
@@ -982,13 +845,15 @@ def main():
switcher = AccountSwitcher()
print("\n=== Cursor账号切换工具 ===")
print("1. 激活并切换账号")
print("2. 仅重置机器码")
print("1. 禁用自动更新")
print("2. 突破使用限制")
print("3. 激活并切换账号")
print("4. 仅重置机器码")
while True:
try:
choice = int(input("\n请选择操作 (1 或 2): ").strip())
if choice in [1, 2]:
choice = int(input("\n请选择操作 (1-4): ").strip())
if choice in [1, 2, 3, 4]:
break
else:
print("无效的选项,请重新输入")
@@ -996,6 +861,18 @@ def main():
print("请输入有效的数字")
if choice == 1:
success, message = switcher.disable_cursor_update()
if success:
print("\n更新已禁用!")
else:
print(f"\n禁用更新失败: {message}")
elif choice == 2:
success, message = switcher.bypass_cursor_limit()
if success:
print("\n突破限制成功!")
else:
print(f"\n突破限制失败: {message}")
elif choice == 3:
activation_code = input("请输入激活码: ").strip()
if switcher.activate_and_switch(activation_code):
print("\n账号激活成功!")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -348,27 +348,103 @@ class CursorAuthManager:
# 确保进程已关闭
if not self.close_cursor_process():
logging.error("无法关闭Cursor进程")
return False
# 等待系统资源释放
time.sleep(3)
# 启动Cursor
if sys.platform == "win32":
cursor_exe = self.cursor_path / "Cursor.exe"
if cursor_exe.exists():
os.startfile(str(cursor_exe))
logging.info("Cursor启动成功")
return True
max_retries = 3
for attempt in range(max_retries):
try:
# 检查是否还有Cursor进程在运行
try:
subprocess.check_output(
"tasklist | findstr Cursor.exe",
startupinfo=subprocess.STARTUPINFO(),
shell=True
)
# 如果找到进程,等待它关闭
if attempt < max_retries - 1:
logging.info("等待进程完全关闭...")
time.sleep(3)
continue
else:
logging.error("无法确保所有Cursor进程已关闭")
return False
except subprocess.CalledProcessError:
# 没有找到进程,可以继续启动
pass
# 使用subprocess启动
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
# 使用subprocess.Popen启动并等待一段时间
process = subprocess.Popen(
str(cursor_exe),
startupinfo=startupinfo,
creationflags=subprocess.CREATE_NEW_CONSOLE
)
# 等待进程启动
time.sleep(5)
# 验证进程是否成功启动
if process.poll() is None: # 进程仍在运行
logging.info("Cursor启动成功")
return True
else:
if attempt < max_retries - 1:
logging.warning(f"启动尝试 {attempt + 1} 失败,准备重试...")
time.sleep(3)
continue
logging.error("Cursor进程启动失败")
# 尝试使用 os.startfile 作为备选方案
try:
os.startfile(str(cursor_exe))
time.sleep(5)
logging.info("使用备选方案启动Cursor")
return True
except Exception as e:
logging.error(f"备选启动方案失败: {str(e)}")
return False
except Exception as e:
if attempt < max_retries - 1:
logging.warning(f"启动尝试 {attempt + 1} 失败: {str(e)},准备重试...")
time.sleep(3)
continue
logging.error(f"启动Cursor失败: {str(e)}")
return False
else:
logging.error(f"未找到Cursor程序: {cursor_exe}")
return False
elif sys.platform == "darwin":
subprocess.run("open -a Cursor", shell=True)
logging.info("Cursor启动成功")
return True
try:
subprocess.run("open -a Cursor", shell=True, check=True)
time.sleep(5)
logging.info("Cursor启动成功")
return True
except subprocess.CalledProcessError as e:
logging.error(f"启动Cursor失败: {str(e)}")
return False
elif sys.platform == "linux":
subprocess.run("cursor &", shell=True)
logging.info("Cursor启动成功")
return True
try:
subprocess.run("cursor &", shell=True, check=True)
time.sleep(5)
logging.info("Cursor启动成功")
return True
except subprocess.CalledProcessError as e:
logging.error(f"启动Cursor失败: {str(e)}")
return False
return False
except Exception as e:

View File

@@ -40,10 +40,9 @@ Write-Host @"
"@
Write-Host "$BLUE================================$NC"
Write-Host "$GREEN Cursor 设备ID 修改工具 $NC"
Write-Host "$YELLOW 关注公众号【煎饼果子卷AI】 $NC"
Write-Host "$YELLOW 一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) $NC"
Write-Host "$YELLOW [重要提示] 本工具免费如果对您有帮助请关注公众号【煎饼果子卷AI】 $NC"
Write-Host "$GREEN 听泉Cursor助手 - 机器码重置工具 $NC"
Write-Host "$YELLOW 官方网站: cursor.nosqli.com $NC"
Write-Host "$YELLOW 微信客服: behikcigar $NC"
Write-Host "$BLUE================================$NC"
Write-Host ""
@@ -191,26 +190,81 @@ $randomPart = Get-RandomHex -length 32
$MACHINE_ID = "$prefixHex$randomPart"
$SQM_ID = "{$([System.Guid]::NewGuid().ToString().ToUpper())}"
# 在生成新ID后直接执行注册表操作移除询问
# 在Update-MachineGuid函数前添加权限检查
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Host "$RED[错误]$NC 请使用管理员权限运行此脚本"
Start-Process powershell "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs
exit
}
function Update-MachineGuid {
try {
$newMachineGuid = [System.Guid]::NewGuid().ToString()
# 先检查注册表路径是否存在
$registryPath = "HKLM:\SOFTWARE\Microsoft\Cryptography"
if (-not (Test-Path $registryPath)) {
throw "注册表路径不存在: $registryPath"
}
# 获取当前的 MachineGuid
$currentGuid = Get-ItemProperty -Path $registryPath -Name MachineGuid -ErrorAction Stop
if (-not $currentGuid) {
throw "无法获取当前的 MachineGuid"
}
$originalGuid = $currentGuid.MachineGuid
Write-Host "$GREEN[信息]$NC 当前注册表值:"
Write-Host "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography"
Write-Host " MachineGuid REG_SZ $originalGuid"
# 创建备份目录(如果不存在)
if (-not (Test-Path $BACKUP_DIR)) {
New-Item -ItemType Directory -Path $BACKUP_DIR -Force | Out-Null
}
# 创建备份文件
$backupFile = "$BACKUP_DIR\MachineGuid_$(Get-Date -Format 'yyyyMMdd_HHmmss').reg"
$backupResult = Start-Process "reg.exe" -ArgumentList "export", "`"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography`"", "`"$backupFile`"" -NoNewWindow -Wait -PassThru
# 备份原始值
$originalGuid = (Get-ItemProperty -Path $registryPath -Name "MachineGuid").MachineGuid
$backupFile = "$BACKUP_DIR\MachineGuid.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
$originalGuid | Out-File $backupFile -Encoding UTF8
if ($backupResult.ExitCode -eq 0) {
Write-Host "$GREEN[信息]$NC 注册表项已备份到:$backupFile"
} else {
Write-Host "$YELLOW[警告]$NC 备份创建失败,继续执行..."
}
# 生成新GUID
$newGuid = [System.Guid]::NewGuid().ToString()
# 更新注册表
Set-ItemProperty -Path $registryPath -Name "MachineGuid" -Value $newMachineGuid
Write-Host "$GREEN[信息]$NC 已更新系统 MachineGuid: $newMachineGuid"
Write-Host "$GREEN[信息]$NC 原始值已备份至: $backupFile"
Write-Host "$GREEN[信息]$NC 注册表路径: 计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography"
Write-Host "$GREEN[信息]$NC 注册表项名: MachineGuid"
Set-ItemProperty -Path $registryPath -Name MachineGuid -Value $newGuid -Force -ErrorAction Stop
# 验证更新
$verifyGuid = (Get-ItemProperty -Path $registryPath -Name MachineGuid -ErrorAction Stop).MachineGuid
if ($verifyGuid -ne $newGuid) {
throw "注册表验证失败:更新后的值 ($verifyGuid) 与预期值 ($newGuid) 不匹配"
}
Write-Host "$GREEN[信息]$NC 注册表更新成功:"
Write-Host "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography"
Write-Host " MachineGuid REG_SZ $newGuid"
return $true
}
catch {
Write-Host "$RED[错误]$NC 更新系统 MachineGuid 失败: $_"
Write-Host "$RED[错误]$NC 注册表操作失败:$($_.Exception.Message)"
# 尝试恢复备份
if ($backupFile -and (Test-Path $backupFile)) {
Write-Host "$YELLOW[恢复]$NC 正在从备份恢复..."
$restoreResult = Start-Process "reg.exe" -ArgumentList "import", "`"$backupFile`"" -NoNewWindow -Wait -PassThru
if ($restoreResult.ExitCode -eq 0) {
Write-Host "$GREEN[恢复成功]$NC 已还原原始注册表值"
} else {
Write-Host "$RED[错误]$NC 恢复失败,请手动导入备份文件:$backupFile"
}
} else {
Write-Host "$YELLOW[警告]$NC 未找到备份文件或备份创建失败,无法自动恢复"
}
return $false
}
}
@@ -297,7 +351,7 @@ try {
# 显示公众号信息
Write-Host ""
Write-Host "$GREEN================================$NC"
Write-Host "$YELLOW 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) $NC"
Write-Host "$YELLOW 欢迎使用Cursor助手 $NC"
Write-Host "$GREEN================================$NC"
Write-Host ""
Write-Host "$GREEN[信息]$NC 请重启 Cursor 以应用新的配置"
@@ -413,6 +467,9 @@ try {
Write-Host "$GREEN[信息]$NC 保持默认设置,不进行更改"
}
# 保留有效的注册表更新
Update-MachineGuid
} catch {
Write-Host "$RED[错误]$NC 主要操作失败: $_"
Write-Host "$YELLOW[尝试]$NC 使用备选方法..."

83
disable_update.ps1 Normal file
View File

@@ -0,0 +1,83 @@
# 检查管理员权限
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
Write-Host "`n[错误] 请以管理员身份运行此脚本" -ForegroundColor Red
Write-Host "请使用 'Start-Process pwsh -Verb RunAs' 启动管理员PowerShell" -ForegroundColor Yellow
Pause
exit
}
Write-Host "`n=== Cursor更新禁用工具 ===" -ForegroundColor Cyan
# 定义路径
$updaterPath = Join-Path $env:LOCALAPPDATA "cursor-updater"
$cursorPath = Join-Path $env:LOCALAPPDATA "Programs\cursor"
$packageJsonPath = Join-Path $cursorPath "resources\app\package.json"
Write-Host "`n正在检查路径..." -ForegroundColor Gray
Write-Host "updater路径: $updaterPath" -ForegroundColor Gray
try {
# 1. 删除现有的updater文件/目录
if (Test-Path $updaterPath) {
Write-Host "`n发现现有updater文件/目录,正在删除..." -ForegroundColor Yellow
Remove-Item -Path $updaterPath -Force -Recurse -ErrorAction Stop
Write-Host "成功删除现有文件/目录" -ForegroundColor Green
}
# 2. 创建空文件
Write-Host "`n正在创建updater文件..." -ForegroundColor Yellow
New-Item -Path $updaterPath -ItemType File -Force -ErrorAction Stop | Out-Null
Write-Host "成功创建updater文件" -ForegroundColor Green
# 3. 设置文件权限
Write-Host "`n正在设置文件权限..." -ForegroundColor Yellow
# 获取当前用户
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
# 创建新的ACL
$acl = New-Object System.Security.AccessControl.FileSecurity
$acl.SetAccessRuleProtection($true, $false) # 禁用继承
# 添加只读权限规则
$readRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
$currentUser,
[System.Security.AccessControl.FileSystemRights]::Read,
[System.Security.AccessControl.AccessControlType]::Allow
)
$acl.AddAccessRule($readRule)
# 应用ACL
Set-Acl -Path $updaterPath -AclObject $acl -ErrorAction Stop
# 设置只读属性
Set-ItemProperty -Path $updaterPath -Name IsReadOnly -Value $true -ErrorAction Stop
Write-Host "成功设置文件权限" -ForegroundColor Green
# 4. 修改package.json
if (Test-Path $packageJsonPath) {
Write-Host "`n正在修改package.json..." -ForegroundColor Yellow
$json = Get-Content $packageJsonPath -Raw | ConvertFrom-Json
$json.updateUrl = ""
$json.disableUpdate = $true
$json | ConvertTo-Json -Depth 10 | Set-Content $packageJsonPath -Encoding UTF8
Write-Host "成功修改package.json" -ForegroundColor Green
}
# 5. 验证权限
Write-Host "`n正在验证文件权限..." -ForegroundColor Yellow
$item = Get-Item $updaterPath
if (-not $item.IsReadOnly) {
throw "文件权限验证失败:文件不是只读"
}
Write-Host "文件权限验证通过" -ForegroundColor Green
Write-Host "`n[成功] Cursor更新已禁用" -ForegroundColor Green
} catch {
Write-Host "`n[错误] 操作失败: $($_.Exception.Message)" -ForegroundColor Red
}
Write-Host "`n按任意键退出..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

View File

@@ -6,9 +6,9 @@ from PIL import Image
from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QLineEdit, QPushButton, QFrame, QTextEdit,
QMessageBox, QApplication, QSystemTrayIcon, QMenu,
QDialog, QProgressBar, QStyle)
QDialog, QProgressBar, QStyle, QDialogButtonBox, QToolTip)
from PyQt5.QtCore import Qt, QTimer, QThread, pyqtSignal
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtGui import QIcon, QPixmap, QCursor
import time
import requests
from urllib.parse import quote
@@ -608,6 +608,30 @@ class MainWindow(QMainWindow):
disable_update_btn.setMinimumWidth(500)
btn_layout.addWidget(disable_update_btn, 0, Qt.AlignCenter)
# 重启Cursor按钮
self.restart_cursor_btn = QPushButton("重启Cursor")
self.restart_cursor_btn.setFixedHeight(30)
self.restart_cursor_btn.setStyleSheet("""
QPushButton {
background-color: #2d8cf0;
border: none;
color: white;
padding: 5px 15px;
border-radius: 4px;
font-size: 13px;
}
QPushButton:hover {
background-color: #2b85e4;
}
QPushButton:pressed {
background-color: #2979d9;
}
QPushButton:disabled {
background-color: #bbbec4;
}
""")
self.restart_cursor_btn.clicked.connect(self.restart_cursor)
main_layout.addWidget(btn_frame)
# 检查更新按钮
@@ -1336,91 +1360,11 @@ class MainWindow(QMainWindow):
try:
# 显示加载对话框
self.show_loading_dialog("正在禁用更新,请稍候...")
self.show_loading_dialog("正在禁用更新...")
# 创建工作线程
from utils.cursor_registry import CursorRegistry
registry = CursorRegistry()
def disable_func():
try:
# 1. 先关闭所有Cursor进程
if sys.platform == "win32":
# 创建startupinfo对象来隐藏命令行窗口
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
# 关闭Cursor
subprocess.run(
"taskkill /f /im Cursor.exe >nul 2>&1",
startupinfo=startupinfo,
shell=True
)
time.sleep(2)
# 2. 处理updater文件
updater_path = Path(os.getenv('LOCALAPPDATA')) / "cursor-updater"
try:
# 如果是目录,则删除
if updater_path.is_dir():
import shutil
shutil.rmtree(str(updater_path))
logging.info("删除updater目录成功")
# 如果是文件,则删除
if updater_path.is_file():
updater_path.unlink()
logging.info("删除updater文件成功")
# 创建阻止文件
updater_path.touch()
logging.info("创建updater空文件成功")
# 设置文件权限
import subprocess
import stat
# 设置只读属性
os.chmod(str(updater_path), stat.S_IREAD)
logging.info("设置只读属性成功")
# 使用icacls设置权限只读
username = os.getenv('USERNAME')
cmd = f'icacls "{str(updater_path)}" /inheritance:r /grant:r "{username}:(R)"'
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if result.returncode != 0:
logging.error(f"设置文件权限失败: {result.stderr}")
return False, "设置文件权限失败"
logging.info("设置文件权限成功")
# 验证设置
if not os.path.exists(updater_path):
return False, "文件创建失败"
if os.access(str(updater_path), os.W_OK):
return False, "文件权限设置失败"
except Exception as e:
logging.error(f"处理updater文件失败: {str(e)}")
return False, "处理updater文件失败"
# 3. 修改package.json配置
if not registry.fix_cursor_startup():
return False, "修改配置失败"
# 4. 重启Cursor
cursor_exe = registry.cursor_path / "Cursor.exe"
if cursor_exe.exists():
os.startfile(str(cursor_exe))
logging.info("Cursor重启成功")
return True, "Cursor更新已禁用程序已重启"
else:
return False, "未找到Cursor程序"
except Exception as e:
logging.error(f"禁用更新时发生错误: {str(e)}")
return False, str(e)
self.worker = ApiWorker(disable_func)
self.worker.finished.connect(lambda result: self.on_disable_update_complete(result))
self.worker = ApiWorker(self.switcher.disable_cursor_update)
self.worker.finished.connect(self.on_disable_update_complete)
self.worker.start()
except Exception as e:
@@ -1429,167 +1373,243 @@ class MainWindow(QMainWindow):
self.show_custom_error("禁用更新失败", str(e))
def on_disable_update_complete(self, result):
"""禁用更新完成回调"""
success, data = result
self.hide_loading_dialog()
self._request_complete()
if success:
self.show_custom_message(
"成功",
"禁用更新成功",
data,
QStyle.SP_DialogApplyButton,
"#198754"
)
else:
self.show_custom_error("禁用更新失败", str(data))
"""禁用更新完成回调"""
try:
success, data = result
logging.info(f"禁用更新操作结果: success={success}, data={data}")
self.hide_loading_dialog()
self._request_complete()
if isinstance(data, tuple):
inner_success, inner_message = data
if inner_success:
self.show_custom_message(
"成功",
"禁用更新成功",
inner_message,
QMessageBox.Information,
"#198754"
)
QTimer.singleShot(1000, self.check_status)
else:
# 创建自定义对话框
dialog = QDialog(self)
dialog.setWindowTitle("禁用更新失败")
dialog.setFixedWidth(400)
layout = QVBoxLayout()
# 警告图标和标题
header_layout = QHBoxLayout()
warning_icon = QLabel()
warning_icon.setPixmap(self.style().standardIcon(QStyle.SP_MessageBoxWarning).pixmap(32, 32))
header_layout.addWidget(warning_icon)
header_label = QLabel("需要手动操作")
header_label.setStyleSheet("color: red; font-size: 16px; font-weight: bold;")
header_layout.addWidget(header_label)
header_layout.addStretch()
layout.addLayout(header_layout)
# 分隔线
line = QFrame()
line.setFrameShape(QFrame.HLine)
line.setFrameShadow(QFrame.Sunken)
layout.addWidget(line)
# 手动步骤说明
steps_label = QLabel(
"请按以下步骤手动操作:\n\n"
"1. 按下 Win + R 组合键\n"
"2. 在运行框中输入 powershell 或 pwsh\n"
"3. 按 Ctrl + Shift + Enter 以管理员身份运行\n"
"4. 在管理员终端中输入以下命令:"
)
steps_label.setWordWrap(True)
steps_label.setStyleSheet("margin: 10px 0;")
layout.addWidget(steps_label)
# 命令文本框和复制按钮
command_layout = QHBoxLayout()
command_text = QLineEdit()
command_text.setText("irm https://github.com/maticarmy/cursor-nosqli-tools/blob/main/scripts/run/cursor_win_id_modifier.ps1 | iex")
command_text.setReadOnly(True)
command_layout.addWidget(command_text)
copy_button = QPushButton("复制")
copy_button.setStyleSheet("""
QPushButton {
background-color: #007bff;
color: white;
border: none;
padding: 5px 15px;
border-radius: 3px;
}
QPushButton:hover {
background-color: #0056b3;
}
""")
copy_button.clicked.connect(lambda: self.copy_and_show_tip(command_text, command_text.text(), "命令已复制到剪贴板"))
command_layout.addWidget(copy_button)
layout.addLayout(command_layout)
# 按钮
button_box = QDialogButtonBox(QDialogButtonBox.Ok)
button_box.accepted.connect(dialog.accept)
layout.addWidget(button_box)
dialog.setLayout(layout)
dialog.exec_()
else:
self.show_custom_error("禁用更新失败", str(data))
except Exception as e:
logging.error(f"处理禁用更新回调时发生错误: {str(e)}")
self.show_custom_error("错误", "处理结果时发生错误,请重试")
def bypass_cursor_limit(self):
"""突破Cursor版本限制"""
if not self.check_activation_status():
return
if not self._check_request_throttle():
return
# 显示加载对话框
self.show_loading_dialog("正在突破限制...")
# 创建工作线程
self.worker = ApiWorker(self.switcher.bypass_cursor_limit)
self.worker.finished.connect(self.on_bypass_limit_complete)
self.worker.start()
def on_bypass_limit_complete(self, result):
"""突破限制完成回调"""
try:
# 显示加载对话框
self.show_loading_dialog("正在突破版本限制,请稍候...")
success, data = result
logging.info(f"突破限制操作结果: success={success}, data={data}")
# 创建工作线程
from utils.cursor_registry import CursorRegistry
registry = CursorRegistry()
# 确保在主线程中执行 UI 操作
self.hide_loading_dialog()
self._request_complete()
def reset_func():
try:
# 1. 先关闭所有Cursor进程
if sys.platform == "win32":
os.system("taskkill /f /im Cursor.exe >nul 2>&1")
time.sleep(2)
if isinstance(data, tuple):
inner_success, inner_message = data
if inner_success:
try:
logging.info("准备显示成功消息对话框")
self.show_custom_message(
"成功",
"突破限制成功",
inner_message,
QStyle.SP_DialogApplyButton,
"#198754"
)
logging.info("成功消息对话框显示完成")
# 更新状态显示
QTimer.singleShot(1000, self.check_status)
logging.info("已安排状态更新")
except Exception as e:
logging.error(f"显示成功消息时发生错误: {str(e)}")
# 使用更简单的消息框作为后备方案
QMessageBox.information(self, "成功", "突破限制成功")
else:
try:
logging.info("准备显示错误消息对话框")
self.show_custom_error("突破限制失败", str(inner_message))
logging.info("错误消息对话框显示完成")
except Exception as e:
logging.error(f"显示错误消息时发生错误: {str(e)}")
# 使用更简单的消息框作为后备方案
QMessageBox.critical(self, "错误", f"突破限制失败: {str(inner_message)}")
else:
self.show_custom_error("突破限制失败", str(data))
# 2. 清理注册表
if not registry.clean_registry():
return False, "清理注册表失败"
# 3. 清理文件
if not registry.clean_cursor_files():
return False, "清理文件失败"
# 4. 重启Cursor
cursor_exe = self.cursor_path / "Cursor.exe"
if cursor_exe.exists():
os.startfile(str(cursor_exe))
logging.info("Cursor重启成功")
return True, "突破限制成功"
else:
return False, "未找到Cursor程序"
except Exception as e:
logging.error(f"突破限制时发生错误: {str(e)}")
return False, str(e)
except Exception as e:
logging.error(f"处理突破限制回调时发生错误: {str(e)}")
try:
self.hide_loading_dialog()
self._request_complete()
QMessageBox.critical(self, "错误", "处理结果时发生错误,请重试")
except:
pass
def show_custom_message(self, title, header, message, icon_type, color, show_copy_button=False, copy_text=None):
"""显示自定义消息对话框
Args:
show_copy_button: 是否显示复制按钮
copy_text: 要复制的文本内容
"""
try:
logging.info(f"准备显示自定义消息框: {header}")
self.worker = ApiWorker(reset_func)
self.worker.finished.connect(self.on_bypass_complete)
self.worker.start()
dialog = QDialog(self)
dialog.setWindowTitle(title)
dialog.setFixedWidth(500)
layout = QVBoxLayout()
# 标题标签
header_label = QLabel(header)
header_label.setStyleSheet(f"color: {color}; font-size: 16px; font-weight: bold;")
layout.addWidget(header_label)
# 消息文本
message_label = QLabel(message)
message_label.setWordWrap(True)
message_label.setStyleSheet("margin: 10px 0;")
layout.addWidget(message_label)
# 添加复制按钮
if show_copy_button and copy_text:
copy_button = QPushButton("复制命令")
copy_button.setStyleSheet("""
QPushButton {
background-color: #007bff;
color: white;
border: none;
padding: 5px 15px;
border-radius: 3px;
}
QPushButton:hover {
background-color: #0056b3;
}
""")
copy_button.clicked.connect(lambda: self.copy_to_clipboard(copy_text))
layout.addWidget(copy_button, alignment=Qt.AlignCenter)
# 确定按钮
button_box = QDialogButtonBox(QDialogButtonBox.Ok)
button_box.accepted.connect(dialog.accept)
layout.addWidget(button_box)
dialog.setLayout(layout)
dialog.exec_()
logging.info("自定义消息框已关闭")
except Exception as e:
self._request_complete()
self.hide_loading_dialog()
self.show_custom_error("突破限制失败", str(e))
def on_bypass_complete(self, result):
"""突破限制完成回调"""
success, data = result
self.hide_loading_dialog()
self._request_complete()
if success:
self.show_custom_message(
"成功",
"突破限制成功",
"Cursor版本限制已突破编辑器已重启。",
QStyle.SP_DialogApplyButton,
"#198754"
)
else:
self.show_custom_error("突破限制失败", str(data))
logging.error(f"显示自定义消息框时发生错误: {str(e)}")
QMessageBox.critical(self, title, message)
def copy_to_clipboard(self, text):
"""复制文本到剪贴板"""
clipboard = QApplication.clipboard()
clipboard.setText(text)
QToolTip.showText(QCursor.pos(), "已复制到剪贴板", None, 2000)
def show_custom_message(self, title, header, message, icon_type, color):
"""显示自定义消息框"""
msg = QDialog(self)
msg.setWindowTitle(title)
msg.setFixedWidth(400)
msg.setWindowFlags(msg.windowFlags() & ~Qt.WindowContextHelpButtonHint)
layout = QVBoxLayout()
# 添加图标
icon_label = QLabel()
icon_label.setPixmap(self.style().standardIcon(icon_type).pixmap(32, 32))
icon_label.setAlignment(Qt.AlignCenter)
layout.addWidget(icon_label)
# 添加标题
text_label = QLabel(header)
text_label.setAlignment(Qt.AlignCenter)
text_label.setStyleSheet(f"""
font-size: 14px;
font-weight: bold;
color: {color};
padding: 10px;
""")
layout.addWidget(text_label)
# 添加详细信息
info_label = QLabel(message)
info_label.setAlignment(Qt.AlignLeft)
info_label.setWordWrap(True)
info_label.setStyleSheet("""
QLabel {
color: #333333;
font-size: 14px;
padding: 15px;
background-color: #f8f9fa;
border-radius: 4px;
border: 1px solid #dee2e6;
margin: 10px;
}
""")
layout.addWidget(info_label)
# 确定按钮
btn_layout = QHBoxLayout()
ok_btn = QPushButton("确定")
ok_btn.clicked.connect(msg.accept)
ok_btn.setStyleSheet(f"""
QPushButton {{
background-color: {color};
color: white;
border: none;
padding: 8px 25px;
border-radius: 4px;
font-size: 13px;
min-width: 100px;
}}
QPushButton:hover {{
background-color: {color.replace('fd', 'd7') if 'fd' in color else color.replace('54', '47')};
}}
""")
btn_layout.addWidget(ok_btn)
layout.addLayout(btn_layout)
msg.setLayout(layout)
msg.exec_()
def show_custom_error(self, header, message):
"""显示自定义错误消息框"""
self.show_custom_message(
"错误",
header,
message,
QStyle.SP_MessageBoxCritical,
"#dc3545"
)
try:
logging.info(f"准备显示错误消息框: {header}")
self.show_custom_message(
"错误",
header,
message,
QStyle.SP_MessageBoxCritical,
"#dc3545"
)
logging.info("错误消息框显示完成")
except Exception as e:
logging.error(f"显示自定义错误消息框失败: {str(e)}")
QMessageBox.critical(self, "错误", f"{header}\n\n{message}")
def _check_request_throttle(self) -> bool:
"""检查是否可以发送请求(防重复提交)
@@ -2028,4 +2048,55 @@ class MainWindow(QMainWindow):
# 更新状态显示
self.check_status()
else:
logging.error(f"心跳发送失败: {message}")
logging.error(f"心跳发送失败: {message}")
def restart_cursor(self):
"""重启Cursor进程"""
try:
self.restart_cursor_btn.setEnabled(False)
self.show_loading_dialog("正在重启Cursor...")
def restart_func():
try:
# 先关闭进程
if not self.switcher.close_cursor_process():
return False, "无法关闭Cursor进程请手动关闭后重试"
# 等待资源释放
time.sleep(2)
# 重启Cursor
if not self.switcher.restart_cursor():
return False, "重启Cursor失败请手动启动Cursor"
return True, "Cursor重启成功"
except Exception as e:
logging.error(f"重启Cursor失败: {str(e)}")
return False, f"重启失败: {str(e)}"
# 创建工作线程
worker = ApiWorker(restart_func)
worker.finished.connect(self.on_restart_complete)
worker.start()
except Exception as e:
self.restart_cursor_btn.setEnabled(True)
self.hide_loading_dialog()
self.show_custom_error("重启失败", str(e))
def on_restart_complete(self, result):
"""重启完成的回调函数"""
success, message = result
self.restart_cursor_btn.setEnabled(True)
self.hide_loading_dialog()
if success:
self.show_custom_message(
"重启成功",
"Cursor已重启",
message,
QMessageBox.Information,
"#2d8cf0"
)
else:
self.show_custom_error("重启失败", message)

210
test_disable_update.py Normal file
View File

@@ -0,0 +1,210 @@
import os
import sys
import json
import ctypes
import logging
import subprocess
from pathlib import Path
from datetime import datetime
from typing import Optional, Tuple, Dict
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
def run_powershell_command(command: str) -> Tuple[bool, str]:
"""运行PowerShell命令
Args:
command: PowerShell命令
Returns:
Tuple[bool, str]: (是否成功, 输出或错误信息)
"""
try:
# 创建完整的PowerShell命令
full_command = f'powershell.exe -Command "{command}"'
# 运行命令
result = subprocess.run(
full_command,
capture_output=True,
text=True,
shell=True
)
if result.returncode == 0:
return True, result.stdout.strip()
else:
return False, result.stderr.strip()
except Exception as e:
return False, str(e)
def is_admin():
"""检查是否具有管理员权限"""
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
def run_as_admin():
"""以管理员权限重新运行程序"""
try:
if not is_admin():
# 获取当前脚本的路径
script = sys.argv[0]
params = ' '.join(sys.argv[1:])
# 以管理员权限重新运行
ctypes.windll.shell32.ShellExecuteW(
None,
"runas",
sys.executable,
f'"{script}" {params}',
None,
1
)
return True
except Exception as e:
logging.error(f"以管理员权限运行失败: {str(e)}")
return False
class CursorUpdateDisabler:
"""专门用于测试禁用Cursor更新的类"""
def __init__(self):
self.localappdata = os.getenv('LOCALAPPDATA')
self.cursor_path = Path(self.localappdata) / "Programs" / "cursor"
self.app_path = self.cursor_path / "resources" / "app"
self.package_json = self.app_path / "package.json"
self.updater_path = Path(self.localappdata) / "cursor-updater"
def disable_auto_update(self) -> Tuple[bool, str]:
"""禁用自动更新
Returns:
Tuple[bool, str]: (是否成功, 消息)
"""
try:
logging.info(f"开始禁用更新操作...")
logging.info(f"updater路径: {self.updater_path}")
# 1. 使用PowerShell强制删除现有文件/目录
if self.updater_path.exists():
logging.info("发现现有updater文件/目录,尝试强制删除...")
# 先获取完全控制权限
take_control_cmd = f'$path = "{self.updater_path}"; $acl = Get-Acl $path; $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name; $fileSystemRights = [System.Security.AccessControl.FileSystemRights]::FullControl; $type = [System.Security.AccessControl.AccessControlType]::Allow; $rule = New-Object System.Security.AccessControl.FileSystemAccessRule($identity, $fileSystemRights, $type); $acl.SetAccessRule($rule); Set-Acl -Path $path -AclObject $acl'
success, output = run_powershell_command(take_control_cmd)
if not success:
logging.warning(f"设置完全控制权限失败: {output}")
# 强制删除
remove_cmd = f'Remove-Item -Path "{self.updater_path}" -Force -Recurse'
success, output = run_powershell_command(remove_cmd)
if not success:
logging.error(f"强制删除失败: {output}")
return False, f"删除现有文件失败: {output}"
logging.info("成功删除现有文件/目录")
# 2. 创建空文件
try:
with open(self.updater_path, 'w') as f:
pass
logging.info("成功创建updater空文件")
except Exception as e:
logging.error(f"创建updater文件失败: {str(e)}")
return False, f"创建updater文件失败: {str(e)}"
# 3. 设置文件权限
try:
# 设置文件为只读并禁止修改
protect_cmd = f'$path = "{self.updater_path}"; $acl = Get-Acl $path; $acl.SetAccessRuleProtection($true, $false); $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name; $fileSystemRights = [System.Security.AccessControl.FileSystemRights]::Read; $type = [System.Security.AccessControl.AccessControlType]::Allow; $rule = New-Object System.Security.AccessControl.FileSystemAccessRule($identity, $fileSystemRights, $type); $acl.AddAccessRule($rule); Set-Acl -Path $path -AclObject $acl'
success, output = run_powershell_command(protect_cmd)
if not success:
logging.error(f"设置文件权限失败: {output}")
return False, f"设置文件权限失败: {output}"
logging.info("成功设置文件权限")
# 设置文件为只读
os.chmod(str(self.updater_path), 0o444) # 设置为只读
logging.info("成功设置文件只读属性")
except Exception as e:
logging.error(f"设置文件权限失败: {str(e)}")
return False, f"设置文件权限失败: {str(e)}"
# 4. 修改package.json
try:
if self.package_json.exists():
with open(self.package_json, "r", encoding="utf-8") as f:
data = json.load(f)
data["updateUrl"] = ""
data["disableUpdate"] = True
with open(self.package_json, "w", encoding="utf-8", newline='\n') as f:
json.dump(data, f, indent=2)
logging.info("成功修改package.json配置")
except Exception as e:
logging.warning(f"修改package.json失败: {str(e)}")
# 5. 验证文件权限
try:
if not self.updater_path.exists():
return False, "updater文件不存在"
# 验证文件是否为只读
if os.access(str(self.updater_path), os.W_OK):
logging.error("文件权限验证失败:文件可写")
return False, "文件权限设置失败:文件仍然可写"
logging.info("文件权限验证通过")
except Exception as e:
logging.error(f"验证文件权限失败: {str(e)}")
return False, f"验证文件权限失败: {str(e)}"
logging.info("禁用自动更新成功完成")
return True, "Cursor更新已禁用"
except Exception as e:
error_msg = f"禁用自动更新失败: {str(e)}"
logging.error(error_msg)
return False, error_msg
def main():
"""主函数"""
try:
# 检查管理员权限
if os.name == 'nt':
import ctypes
if not is_admin():
logging.warning("当前不是管理员权限运行")
print("\n[错误] 请按以下步骤手动操作:")
print("1. 按下 Win + R 组合键")
print("2. 在运行框中输入 powershell 或 pwsh")
print("3. 按 Ctrl + Shift + Enter 以管理员身份运行")
print("4. 在管理员终端中输入以下命令:")
print(" irm https://github.com/maticarmy/cursor-nosqli-tools/blob/main/scripts/run/cursor_win_id_modifier.ps1 | iex")
input("\n按回车键退出...")
return
print("\n=== Cursor更新禁用测试工具 ===")
disabler = CursorUpdateDisabler()
success, message = disabler.disable_auto_update()
if success:
print("\n更新已禁用!")
else:
print(f"\n禁用更新失败: {message}")
except Exception as e:
logging.error(f"程序执行出错: {str(e)}")
print(f"\n程序执行出错: {str(e)}")
finally:
input("\n按回车键退出...")
if __name__ == "__main__":
main()

View File

@@ -143,38 +143,101 @@ class CursorResetter:
logging.error(f"更新配置文件失败: {str(e)}")
return False
def disable_auto_update(self) -> bool:
"""禁用自动更新"""
def disable_auto_update(self) -> Tuple[bool, str]:
"""禁用自动更新
Returns:
Tuple[bool, str]: (是否成功, 消息)
"""
try:
updater_path = Path(self.localappdata) / "cursor-updater"
# 删除现有文件/目录
if updater_path.exists():
if updater_path.is_dir():
import shutil
shutil.rmtree(updater_path)
else:
updater_path.unlink()
# 1. 先尝试删除现有文件/目录
try:
if updater_path.exists():
if updater_path.is_dir():
import shutil
shutil.rmtree(str(updater_path))
logging.info("已删除updater目录")
else:
updater_path.unlink()
logging.info("已删除updater文件")
except Exception as e:
logging.warning(f"删除现有updater文件/目录失败: {str(e)}")
# 创建空文件并设置只读
updater_path.touch()
import stat
updater_path.chmod(stat.S_IREAD)
# 设置文件权限
if os.name == 'nt':
subprocess.run(
f'icacls "{updater_path}" /inheritance:r /grant:r "{os.getenv("USERNAME")}:(R)"',
shell=True,
check=True
)
# 2. 创建空文件
try:
# 如果文件已存在,先移除它
if updater_path.exists():
try:
os.remove(str(updater_path))
except:
pass
# 创建空文件
with open(updater_path, 'w') as f:
pass
logging.info("已创建updater空文件")
except Exception as e:
logging.error(f"创建updater文件失败: {str(e)}")
return False, f"创建updater文件失败: {str(e)}"
# 3. 设置文件权限
try:
import stat
# 设置文件为只读
os.chmod(str(updater_path), stat.S_IREAD)
logging.info("已设置只读属性")
if os.name == 'nt':
# 使用takeown获取文件所有权
subprocess.run(['takeown', '/f', str(updater_path)], check=True, capture_output=True)
logging.info("已获取文件所有权")
# 使用icacls设置权限
username = os.getenv("USERNAME")
subprocess.run(
['icacls', str(updater_path), '/inheritance:r', '/grant:r', f'{username}:(R)'],
check=True,
capture_output=True
)
logging.info("已设置文件权限")
except Exception as e:
logging.error(f"设置文件权限失败: {str(e)}")
return False, f"设置文件权限失败: {str(e)}"
# 4. 修改package.json
try:
if self.package_json.exists():
with open(self.package_json, "r", encoding="utf-8") as f:
data = json.load(f)
data["updateUrl"] = ""
data["disableUpdate"] = True
with open(self.package_json, "w", encoding="utf-8", newline='\n') as f:
json.dump(data, f, indent=2)
logging.info("已修改package.json配置")
except Exception as e:
logging.warning(f"修改package.json失败: {str(e)}")
# 5. 验证文件权限
try:
if not updater_path.exists():
return False, "updater文件不存在"
if os.access(str(updater_path), os.W_OK):
return False, "文件权限设置失败"
except Exception as e:
logging.error(f"验证文件权限失败: {str(e)}")
return False, f"验证文件权限失败: {str(e)}"
logging.info("已禁用自动更新")
return True
return True, "Cursor更新已禁用"
except Exception as e:
logging.error(f"禁用自动更新失败: {str(e)}")
return False
error_msg = f"禁用自动更新失败: {str(e)}"
logging.error(error_msg)
return False, error_msg
def reset_cursor(self, disable_update: bool = True) -> Tuple[bool, str]:
"""重置Cursor
@@ -201,8 +264,7 @@ class CursorResetter:
return False, "更新系统MachineGuid失败"
# 4. 禁用自动更新(如果需要)
if disable_update and not self.disable_auto_update():
logging.warning("禁用自动更新失败")
# 5. 修改package.json
if self.package_json.exists():

View File

@@ -1 +1 @@
3.5.2
3.5.3