You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2467 lines
108 KiB
2467 lines
108 KiB
# 设置输出编码为 UTF-8
|
|
$OutputEncoding = [System.Text.Encoding]::UTF8
|
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
|
|
# 颜色定义(兼容 PowerShell 5.1 和 7.x)
|
|
$ESC = [char]27
|
|
$RED = "$ESC[31m"
|
|
$GREEN = "$ESC[32m"
|
|
$YELLOW = "$ESC[33m"
|
|
$BLUE = "$ESC[34m"
|
|
$NC = "$ESC[0m"
|
|
|
|
# 启动时尝试调整终端窗口大小为 120x40(列x行);不支持/失败时静默忽略,避免影响脚本主流程
|
|
function Try-ResizeTerminalWindow {
|
|
param(
|
|
[int]$Columns = 120,
|
|
[int]$Rows = 40
|
|
)
|
|
|
|
# 方式1:通过 PowerShell Host RawUI 调整(传统控制台、ConEmu 等可能支持)
|
|
try {
|
|
$rawUi = $null
|
|
if ($Host -and $Host.UI -and $Host.UI.RawUI) {
|
|
$rawUi = $Host.UI.RawUI
|
|
}
|
|
|
|
if ($rawUi) {
|
|
try {
|
|
# BufferSize 必须 >= WindowSize,否则会抛异常
|
|
$bufferSize = $rawUi.BufferSize
|
|
$newBufferSize = New-Object System.Management.Automation.Host.Size (
|
|
([Math]::Max($bufferSize.Width, $Columns)),
|
|
([Math]::Max($bufferSize.Height, $Rows))
|
|
)
|
|
$rawUi.BufferSize = $newBufferSize
|
|
} catch {
|
|
# 静默忽略
|
|
}
|
|
|
|
try {
|
|
$rawUi.WindowSize = New-Object System.Management.Automation.Host.Size ($Columns, $Rows)
|
|
} catch {
|
|
# 静默忽略
|
|
}
|
|
}
|
|
} catch {
|
|
# 静默忽略
|
|
}
|
|
|
|
# 方式2:通过 ANSI 转义序列再尝试一次(Windows Terminal 等可能支持)
|
|
try {
|
|
if (-not [Console]::IsOutputRedirected) {
|
|
$escChar = [char]27
|
|
[Console]::Out.Write("$escChar[8;${Rows};${Columns}t")
|
|
}
|
|
} catch {
|
|
# 静默忽略
|
|
}
|
|
}
|
|
|
|
Try-ResizeTerminalWindow -Columns 120 -Rows 40
|
|
|
|
# 路径解析:优先使用 .NET 获取系统目录,避免环境变量缺失导致路径异常
|
|
function Get-FolderPathSafe {
|
|
param(
|
|
[Parameter(Mandatory = $true)][System.Environment+SpecialFolder]$SpecialFolder,
|
|
[Parameter(Mandatory = $true)][string]$EnvVarName,
|
|
[Parameter(Mandatory = $true)][string]$FallbackRelative,
|
|
[Parameter(Mandatory = $true)][string]$Label
|
|
)
|
|
$path = [Environment]::GetFolderPath($SpecialFolder)
|
|
if ([string]::IsNullOrWhiteSpace($path)) {
|
|
$envValue = [Environment]::GetEnvironmentVariable($EnvVarName)
|
|
if (-not [string]::IsNullOrWhiteSpace($envValue)) {
|
|
$path = $envValue
|
|
}
|
|
}
|
|
if ([string]::IsNullOrWhiteSpace($path)) {
|
|
$userProfile = [Environment]::GetFolderPath([System.Environment+SpecialFolder]::UserProfile)
|
|
if ([string]::IsNullOrWhiteSpace($userProfile)) {
|
|
$userProfile = [Environment]::GetEnvironmentVariable("USERPROFILE")
|
|
}
|
|
if (-not [string]::IsNullOrWhiteSpace($userProfile)) {
|
|
$path = Join-Path $userProfile $FallbackRelative
|
|
}
|
|
}
|
|
if ([string]::IsNullOrWhiteSpace($path)) {
|
|
Write-Host "$YELLOW⚠️ [路径]$NC $Label 无法解析,将尝试其他方式"
|
|
} else {
|
|
Write-Host "$BLUEℹ️ [路径]$NC ${Label}: $path"
|
|
}
|
|
return $path
|
|
}
|
|
|
|
function Initialize-CursorPaths {
|
|
Write-Host "$BLUEℹ️ [路径]$NC 开始解析 Cursor 相关路径..."
|
|
$global:CursorAppDataRoot = Get-FolderPathSafe `
|
|
-SpecialFolder ([System.Environment+SpecialFolder]::ApplicationData) `
|
|
-EnvVarName "APPDATA" `
|
|
-FallbackRelative "AppData\Roaming" `
|
|
-Label "Roaming AppData"
|
|
$global:CursorLocalAppDataRoot = Get-FolderPathSafe `
|
|
-SpecialFolder ([System.Environment+SpecialFolder]::LocalApplicationData) `
|
|
-EnvVarName "LOCALAPPDATA" `
|
|
-FallbackRelative "AppData\Local" `
|
|
-Label "Local AppData"
|
|
$global:CursorUserProfileRoot = [Environment]::GetFolderPath([System.Environment+SpecialFolder]::UserProfile)
|
|
if ([string]::IsNullOrWhiteSpace($global:CursorUserProfileRoot)) {
|
|
$global:CursorUserProfileRoot = [Environment]::GetEnvironmentVariable("USERPROFILE")
|
|
}
|
|
if (-not [string]::IsNullOrWhiteSpace($global:CursorUserProfileRoot)) {
|
|
Write-Host "$BLUEℹ️ [路径]$NC 用户目录: $global:CursorUserProfileRoot"
|
|
}
|
|
$global:CursorAppDataDir = if ($global:CursorAppDataRoot) { Join-Path $global:CursorAppDataRoot "Cursor" } else { $null }
|
|
$global:CursorLocalAppDataDir = if ($global:CursorLocalAppDataRoot) { Join-Path $global:CursorLocalAppDataRoot "Cursor" } else { $null }
|
|
$global:CursorStorageDir = if ($global:CursorAppDataDir) { Join-Path $global:CursorAppDataDir "User\globalStorage" } else { $null }
|
|
$global:CursorStorageFile = if ($global:CursorStorageDir) { Join-Path $global:CursorStorageDir "storage.json" } else { $null }
|
|
$global:CursorBackupDir = if ($global:CursorStorageDir) { Join-Path $global:CursorStorageDir "backups" } else { $null }
|
|
|
|
if ($global:CursorStorageDir -and -not (Test-Path $global:CursorStorageDir)) {
|
|
Write-Host "$YELLOW⚠️ [路径]$NC 全局配置目录不存在: $global:CursorStorageDir"
|
|
}
|
|
if ($global:CursorStorageFile) {
|
|
if (Test-Path $global:CursorStorageFile) {
|
|
Write-Host "$GREEN✅ [路径]$NC 已找到配置文件: $global:CursorStorageFile"
|
|
} else {
|
|
Write-Host "$YELLOW⚠️ [路径]$NC 配置文件不存在: $global:CursorStorageFile"
|
|
}
|
|
}
|
|
}
|
|
|
|
function Normalize-CursorInstallCandidate {
|
|
param([string]$Path)
|
|
if ([string]::IsNullOrWhiteSpace($Path)) {
|
|
return $null
|
|
}
|
|
$candidate = $Path.Trim().Trim('"')
|
|
if (Test-Path $candidate -PathType Leaf) {
|
|
$candidate = Split-Path -Parent $candidate
|
|
}
|
|
return $candidate
|
|
}
|
|
|
|
function Test-CursorInstallPath {
|
|
param([string]$Path)
|
|
$candidate = Normalize-CursorInstallCandidate -Path $Path
|
|
if (-not $candidate) {
|
|
return $false
|
|
}
|
|
$exePath = Join-Path $candidate "Cursor.exe"
|
|
return (Test-Path $exePath)
|
|
}
|
|
|
|
function Get-CursorInstallPathFromRegistry {
|
|
$results = @()
|
|
$uninstallKeys = @(
|
|
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*",
|
|
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*",
|
|
"HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
|
|
)
|
|
foreach ($key in $uninstallKeys) {
|
|
try {
|
|
$items = Get-ItemProperty -Path $key -ErrorAction SilentlyContinue
|
|
foreach ($item in $items) {
|
|
if (-not $item.DisplayName -or $item.DisplayName -notlike "*Cursor*") {
|
|
continue
|
|
}
|
|
$candidate = $null
|
|
if ($item.InstallLocation) {
|
|
$candidate = $item.InstallLocation
|
|
} elseif ($item.DisplayIcon) {
|
|
$candidate = $item.DisplayIcon.Split(',')[0].Trim('"')
|
|
} elseif ($item.UninstallString) {
|
|
$candidate = $item.UninstallString.Split(' ')[0].Trim('"')
|
|
}
|
|
if ($candidate) {
|
|
$results += $candidate
|
|
}
|
|
}
|
|
} catch {
|
|
Write-Host "$YELLOW⚠️ [路径]$NC 读取注册表失败: $key"
|
|
}
|
|
}
|
|
return $results | Where-Object { $_ } | Select-Object -Unique
|
|
}
|
|
|
|
function Request-CursorInstallPathFromUser {
|
|
Write-Host "$YELLOW💡 [提示]$NC 自动检测失败,可手动选择 Cursor 安装目录(包含 Cursor.exe)"
|
|
$selectedPath = $null
|
|
try {
|
|
Add-Type -AssemblyName System.Windows.Forms -ErrorAction Stop
|
|
$dialog = New-Object System.Windows.Forms.FolderBrowserDialog
|
|
$dialog.Description = "请选择 Cursor 安装目录(包含 Cursor.exe)"
|
|
$dialog.ShowNewFolderButton = $false
|
|
if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
|
|
$selectedPath = $dialog.SelectedPath
|
|
}
|
|
} catch {
|
|
Write-Host "$YELLOW⚠️ [提示]$NC 无法打开选择窗口,将使用命令行输入"
|
|
}
|
|
if (-not $selectedPath) {
|
|
$manualInput = Read-Host "请输入 Cursor 安装目录(包含 Cursor.exe),或直接回车取消"
|
|
if (-not [string]::IsNullOrWhiteSpace($manualInput)) {
|
|
$selectedPath = $manualInput
|
|
}
|
|
}
|
|
if ($selectedPath) {
|
|
$normalized = Normalize-CursorInstallCandidate -Path $selectedPath
|
|
if ($normalized -and (Test-CursorInstallPath -Path $normalized)) {
|
|
Write-Host "$GREEN✅ [发现]$NC 手动指定安装路径: $normalized"
|
|
return $normalized
|
|
}
|
|
Write-Host "$RED❌ [错误]$NC 手动路径无效: $selectedPath"
|
|
}
|
|
return $null
|
|
}
|
|
|
|
function Resolve-CursorInstallPath {
|
|
param([switch]$AllowPrompt)
|
|
if ($global:CursorInstallPath -and (Test-CursorInstallPath -Path $global:CursorInstallPath)) {
|
|
return $global:CursorInstallPath
|
|
}
|
|
|
|
Write-Host "$BLUE🔎 [路径]$NC 正在检测 Cursor 安装目录..."
|
|
$candidates = @()
|
|
if ($global:CursorLocalAppDataRoot) {
|
|
$candidates += (Join-Path $global:CursorLocalAppDataRoot "Programs\Cursor")
|
|
}
|
|
$programFiles = [Environment]::GetFolderPath([System.Environment+SpecialFolder]::ProgramFiles)
|
|
if ($programFiles) {
|
|
$candidates += (Join-Path $programFiles "Cursor")
|
|
}
|
|
$programFilesX86 = [Environment]::GetFolderPath([System.Environment+SpecialFolder]::ProgramFilesX86)
|
|
if ($programFilesX86) {
|
|
$candidates += (Join-Path $programFilesX86 "Cursor")
|
|
}
|
|
|
|
$regCandidates = @(Get-CursorInstallPathFromRegistry)
|
|
if ($regCandidates.Count -gt 0) {
|
|
Write-Host "$BLUEℹ️ [路径]$NC 从注册表发现候选路径: $($regCandidates -join '; ')"
|
|
$candidates += $regCandidates
|
|
}
|
|
|
|
$fixedDrives = [IO.DriveInfo]::GetDrives() | Where-Object { $_.DriveType -eq 'Fixed' }
|
|
foreach ($drive in $fixedDrives) {
|
|
$root = $drive.RootDirectory.FullName
|
|
$candidates += (Join-Path $root "Program Files\Cursor")
|
|
$candidates += (Join-Path $root "Program Files (x86)\Cursor")
|
|
$candidates += (Join-Path $root "Cursor")
|
|
}
|
|
|
|
$candidates = $candidates | Where-Object { $_ } | Select-Object -Unique
|
|
$totalCandidates = $candidates.Count
|
|
for ($i = 0; $i -lt $totalCandidates; $i++) {
|
|
$candidate = Normalize-CursorInstallCandidate -Path $candidates[$i]
|
|
$attempt = $i + 1
|
|
if (-not $candidate) {
|
|
continue
|
|
}
|
|
Write-Host "$BLUE⏳ [路径]$NC ($attempt/$totalCandidates) 尝试安装路径: $candidate"
|
|
if (Test-CursorInstallPath -Path $candidate) {
|
|
$global:CursorInstallPath = $candidate
|
|
Write-Host "$GREEN✅ [发现]$NC 找到Cursor安装路径: $candidate"
|
|
return $candidate
|
|
}
|
|
}
|
|
|
|
if ($AllowPrompt) {
|
|
$manualPath = Request-CursorInstallPathFromUser
|
|
if ($manualPath) {
|
|
$global:CursorInstallPath = $manualPath
|
|
return $manualPath
|
|
}
|
|
}
|
|
|
|
Write-Host "$RED❌ [错误]$NC 未找到Cursor应用安装路径"
|
|
Write-Host "$YELLOW💡 [提示]$NC 请确认Cursor已正确安装或手动指定路径"
|
|
return $null
|
|
}
|
|
|
|
# 配置文件路径(初始化后统一使用全局变量)
|
|
Initialize-CursorPaths
|
|
$STORAGE_FILE = $global:CursorStorageFile
|
|
$BACKUP_DIR = $global:CursorBackupDir
|
|
|
|
# PowerShell原生方法生成随机字符串
|
|
function Generate-RandomString {
|
|
param([int]$Length)
|
|
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
$result = ""
|
|
for ($i = 0; $i -lt $Length; $i++) {
|
|
$result += $chars[(Get-Random -Maximum $chars.Length)]
|
|
}
|
|
return $result
|
|
}
|
|
|
|
# 🔍 简易 JavaScript 花括号匹配(用于在限定片段内定位函数边界,避免正则跨段误替换)
|
|
# 说明:这是一个轻量解析器,足以应对 main.js 中的压缩函数体(含 try/catch、字符串、注释)。
|
|
function Find-JsMatchingBraceEnd {
|
|
param(
|
|
[Parameter(Mandatory = $true)][string]$Text,
|
|
[Parameter(Mandatory = $true)][int]$OpenBraceIndex,
|
|
[int]$MaxScan = 20000
|
|
)
|
|
|
|
if ($OpenBraceIndex -lt 0 -or $OpenBraceIndex -ge $Text.Length) {
|
|
return -1
|
|
}
|
|
|
|
$limit = [Math]::Min($Text.Length, $OpenBraceIndex + $MaxScan)
|
|
|
|
$depth = 1
|
|
$inSingle = $false
|
|
$inDouble = $false
|
|
$inTemplate = $false
|
|
$inLineComment = $false
|
|
$inBlockComment = $false
|
|
$escape = $false
|
|
|
|
for ($i = $OpenBraceIndex + 1; $i -lt $limit; $i++) {
|
|
$ch = $Text[$i]
|
|
$next = if ($i + 1 -lt $limit) { $Text[$i + 1] } else { [char]0 }
|
|
|
|
if ($inLineComment) {
|
|
if ($ch -eq "`n") { $inLineComment = $false }
|
|
continue
|
|
}
|
|
if ($inBlockComment) {
|
|
if ($ch -eq '*' -and $next -eq '/') { $inBlockComment = $false; $i++; continue }
|
|
continue
|
|
}
|
|
|
|
if ($inSingle) {
|
|
if ($escape) { $escape = $false; continue }
|
|
if ($ch -eq '\') { $escape = $true; continue }
|
|
if ($ch -eq "'") { $inSingle = $false }
|
|
continue
|
|
}
|
|
if ($inDouble) {
|
|
if ($escape) { $escape = $false; continue }
|
|
if ($ch -eq '\') { $escape = $true; continue }
|
|
if ($ch -eq '"') { $inDouble = $false }
|
|
continue
|
|
}
|
|
if ($inTemplate) {
|
|
if ($escape) { $escape = $false; continue }
|
|
if ($ch -eq '\') { $escape = $true; continue }
|
|
if ($ch -eq '`') { $inTemplate = $false }
|
|
continue
|
|
}
|
|
|
|
# 注释检测(仅在非字符串状态下)
|
|
if ($ch -eq '/' -and $next -eq '/') { $inLineComment = $true; $i++; continue }
|
|
if ($ch -eq '/' -and $next -eq '*') { $inBlockComment = $true; $i++; continue }
|
|
|
|
# 字符串/模板字符串
|
|
if ($ch -eq "'") { $inSingle = $true; continue }
|
|
if ($ch -eq '"') { $inDouble = $true; continue }
|
|
if ($ch -eq '`') { $inTemplate = $true; continue }
|
|
|
|
# 花括号深度
|
|
if ($ch -eq '{') { $depth++; continue }
|
|
if ($ch -eq '}') {
|
|
$depth--
|
|
if ($depth -eq 0) { return $i }
|
|
}
|
|
}
|
|
|
|
return -1
|
|
}
|
|
|
|
# 🔧 修改Cursor内核JS文件实现设备识别绕过(增强版三重方案)
|
|
# 方案A: someValue占位符替换 - 稳定锚点,不依赖混淆后的函数名
|
|
# 方案B: b6 定点重写 - 机器码源函数直接返回固定值
|
|
# 方案C: Loader Stub + 外置 Hook - 主/共享进程仅加载外置 Hook 文件
|
|
function Modify-CursorJSFiles {
|
|
Write-Host ""
|
|
Write-Host "$BLUE🔧 [内核修改]$NC 开始修改Cursor内核JS文件实现设备识别绕过..."
|
|
Write-Host "$BLUE💡 [方案]$NC 使用增强版三重方案:占位符替换 + b6 定点重写 + Loader Stub + 外置 Hook"
|
|
Write-Host ""
|
|
|
|
# Windows版Cursor应用路径(支持自动检测 + 手动兜底)
|
|
$cursorAppPath = Resolve-CursorInstallPath -AllowPrompt
|
|
if (-not $cursorAppPath) {
|
|
return $false
|
|
}
|
|
|
|
# 生成或复用设备标识符(优先使用配置中生成的值)
|
|
$useConfigIds = $false
|
|
if ($global:CursorIds -and $global:CursorIds.machineId -and $global:CursorIds.macMachineId -and $global:CursorIds.devDeviceId -and $global:CursorIds.sqmId) {
|
|
$machineId = [string]$global:CursorIds.machineId
|
|
$macMachineId = [string]$global:CursorIds.macMachineId
|
|
$deviceId = [string]$global:CursorIds.devDeviceId
|
|
$sqmId = [string]$global:CursorIds.sqmId
|
|
# 机器 GUID 用于模拟注册表/原始机器码读取
|
|
$machineGuid = if ($global:CursorIds.machineGuid) { [string]$global:CursorIds.machineGuid } else { [System.Guid]::NewGuid().ToString().ToLower() }
|
|
$sessionId = if ($global:CursorIds.sessionId) { [string]$global:CursorIds.sessionId } else { [System.Guid]::NewGuid().ToString().ToLower() }
|
|
$firstSessionDateValue = if ($global:CursorIds.firstSessionDate) { [string]$global:CursorIds.firstSessionDate } else { (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss.fffZ") }
|
|
$macAddress = if ($global:CursorIds.macAddress) { [string]$global:CursorIds.macAddress } else { "00:11:22:33:44:55" }
|
|
$useConfigIds = $true
|
|
} else {
|
|
$randomBytes = New-Object byte[] 32
|
|
$rng = [System.Security.Cryptography.RNGCryptoServiceProvider]::new()
|
|
$rng.GetBytes($randomBytes)
|
|
$machineId = [System.BitConverter]::ToString($randomBytes) -replace '-',''
|
|
$rng.Dispose()
|
|
$deviceId = [System.Guid]::NewGuid().ToString().ToLower()
|
|
$randomBytes2 = New-Object byte[] 32
|
|
$rng2 = [System.Security.Cryptography.RNGCryptoServiceProvider]::new()
|
|
$rng2.GetBytes($randomBytes2)
|
|
$macMachineId = [System.BitConverter]::ToString($randomBytes2) -replace '-',''
|
|
$rng2.Dispose()
|
|
$sqmId = "{" + [System.Guid]::NewGuid().ToString().ToUpper() + "}"
|
|
# 机器 GUID 用于模拟注册表/原始机器码读取
|
|
$machineGuid = [System.Guid]::NewGuid().ToString().ToLower()
|
|
$sessionId = [System.Guid]::NewGuid().ToString().ToLower()
|
|
$firstSessionDateValue = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
|
|
$macAddress = "00:11:22:33:44:55"
|
|
}
|
|
|
|
if ($useConfigIds) {
|
|
Write-Host "$GREEN🔑 [准备]$NC 已使用配置中的设备标识符"
|
|
} else {
|
|
Write-Host "$GREEN🔑 [生成]$NC 已生成新的设备标识符"
|
|
}
|
|
Write-Host " machineId: $($machineId.Substring(0,16))..."
|
|
Write-Host " machineGuid: $($machineGuid.Substring(0,16))..."
|
|
Write-Host " deviceId: $($deviceId.Substring(0,16))..."
|
|
Write-Host " macMachineId: $($macMachineId.Substring(0,16))..."
|
|
Write-Host " sqmId: $sqmId"
|
|
|
|
# 保存 ID 配置到用户目录(供 Hook 读取)
|
|
# 每次执行都删除旧配置并重新生成,确保获得新的设备标识符
|
|
$idsConfigPath = "$env:USERPROFILE\.cursor_ids.json"
|
|
if (Test-Path $idsConfigPath) {
|
|
Remove-Item -Path $idsConfigPath -Force
|
|
Write-Host "$YELLOW🗑️ [清理]$NC 已删除旧的 ID 配置文件"
|
|
}
|
|
$idsConfig = @{
|
|
machineId = $machineId
|
|
machineGuid = $machineGuid
|
|
macMachineId = $macMachineId
|
|
devDeviceId = $deviceId
|
|
sqmId = $sqmId
|
|
macAddress = $macAddress
|
|
sessionId = $sessionId
|
|
firstSessionDate = $firstSessionDateValue
|
|
createdAt = $firstSessionDateValue
|
|
}
|
|
$idsConfig | ConvertTo-Json | Set-Content -Path $idsConfigPath -Encoding UTF8
|
|
Write-Host "$GREEN💾 [保存]$NC 新的 ID 配置已保存到: $idsConfigPath"
|
|
|
|
# 部署外置 Hook 文件(供 Loader Stub 加载,支持多域名备用下载)
|
|
$hookTargetPath = "$env:USERPROFILE\.cursor_hook.js"
|
|
# 兼容:通过 `irm ... | iex` 执行时 $PSScriptRoot 可能为空,Join-Path 会直接报错
|
|
$hookSourceCandidates = @()
|
|
if (-not [string]::IsNullOrWhiteSpace($PSScriptRoot)) {
|
|
$hookSourceCandidates += (Join-Path $PSScriptRoot "..\hook\cursor_hook.js")
|
|
} elseif ($MyInvocation.MyCommand.Path) {
|
|
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
if (-not [string]::IsNullOrWhiteSpace($scriptDir)) {
|
|
$hookSourceCandidates += (Join-Path $scriptDir "..\hook\cursor_hook.js")
|
|
}
|
|
}
|
|
$cwdPath = $null
|
|
try { $cwdPath = (Get-Location).Path } catch { $cwdPath = $null }
|
|
if (-not [string]::IsNullOrWhiteSpace($cwdPath)) {
|
|
$hookSourceCandidates += (Join-Path $cwdPath "scripts\hook\cursor_hook.js")
|
|
}
|
|
$hookSourcePath = $hookSourceCandidates | Where-Object { $_ -and (Test-Path $_) } | Select-Object -First 1
|
|
$hookDownloadUrls = @(
|
|
"https://wget.la/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/hook/cursor_hook.js",
|
|
"https://down.npee.cn/?https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/hook/cursor_hook.js",
|
|
"https://xget.xi-xu.me/gh/yuaotian/go-cursor-help/refs/heads/master/scripts/hook/cursor_hook.js",
|
|
"https://gh-proxy.com/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/hook/cursor_hook.js",
|
|
"https://gh.chjina.com/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/hook/cursor_hook.js"
|
|
)
|
|
# 支持通过环境变量覆盖下载节点(逗号分隔)
|
|
if ($env:CURSOR_HOOK_DOWNLOAD_URLS) {
|
|
$hookDownloadUrls = $env:CURSOR_HOOK_DOWNLOAD_URLS -split '\s*,\s*' | Where-Object { $_ }
|
|
Write-Host "$BLUEℹ️ [Hook]$NC 检测到自定义下载节点列表,将优先使用"
|
|
}
|
|
if ($hookSourcePath) {
|
|
try {
|
|
Copy-Item -Path $hookSourcePath -Destination $hookTargetPath -Force
|
|
Write-Host "$GREEN✅ [Hook]$NC 外置 Hook 已部署: $hookTargetPath"
|
|
} catch {
|
|
Write-Host "$YELLOW⚠️ [Hook]$NC 本地 Hook 复制失败,尝试在线下载..."
|
|
}
|
|
}
|
|
if (-not (Test-Path $hookTargetPath)) {
|
|
Write-Host "$BLUEℹ️ [Hook]$NC 正在下载外置 Hook,用于设备标识拦截..."
|
|
$originalProgressPreference = $ProgressPreference
|
|
$ProgressPreference = 'Continue'
|
|
try {
|
|
if ($hookDownloadUrls.Count -eq 0) {
|
|
Write-Host "$YELLOW⚠️ [Hook]$NC 下载节点列表为空,跳过在线下载"
|
|
} else {
|
|
$totalUrls = $hookDownloadUrls.Count
|
|
for ($i = 0; $i -lt $totalUrls; $i++) {
|
|
$url = $hookDownloadUrls[$i]
|
|
$attempt = $i + 1
|
|
Write-Host "$BLUE⏳ [Hook]$NC ($attempt/$totalUrls) 当前下载节点: $url"
|
|
try {
|
|
Invoke-WebRequest -Uri $url -OutFile $hookTargetPath -UseBasicParsing -ErrorAction Stop
|
|
Write-Host "$GREEN✅ [Hook]$NC 外置 Hook 已在线下载: $hookTargetPath"
|
|
break
|
|
} catch {
|
|
Write-Host "$YELLOW⚠️ [Hook]$NC 外置 Hook 下载失败: $url"
|
|
if (Test-Path $hookTargetPath) {
|
|
Remove-Item -Path $hookTargetPath -Force
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} finally {
|
|
$ProgressPreference = $originalProgressPreference
|
|
}
|
|
if (-not (Test-Path $hookTargetPath)) {
|
|
Write-Host "$YELLOW⚠️ [Hook]$NC 外置 Hook 全部下载失败"
|
|
}
|
|
}
|
|
|
|
# 目标JS文件列表(Windows路径,按优先级排序)
|
|
$jsFiles = @(
|
|
"$cursorAppPath\resources\app\out\main.js",
|
|
# 共享进程用于聚合 telemetry,需要同步注入
|
|
"$cursorAppPath\resources\app\out\vs\code\electron-utility\sharedProcess\sharedProcessMain.js"
|
|
)
|
|
|
|
$modifiedCount = 0
|
|
|
|
# 关闭Cursor进程
|
|
Write-Host "$BLUE🔄 [关闭]$NC 关闭Cursor进程以进行文件修改..."
|
|
Stop-AllCursorProcesses -MaxRetries 3 -WaitSeconds 3 | Out-Null
|
|
|
|
# 创建备份目录
|
|
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
|
$backupPath = "$cursorAppPath\resources\app\out\backups"
|
|
|
|
Write-Host "$BLUE💾 [备份]$NC 创建Cursor JS文件备份..."
|
|
try {
|
|
New-Item -ItemType Directory -Path $backupPath -Force | Out-Null
|
|
|
|
# 检查是否存在原始备份
|
|
$originalBackup = "$backupPath\main.js.original"
|
|
|
|
foreach ($file in $jsFiles) {
|
|
if (-not (Test-Path $file)) {
|
|
Write-Host "$YELLOW⚠️ [警告]$NC 文件不存在: $(Split-Path $file -Leaf)"
|
|
continue
|
|
}
|
|
|
|
$fileName = Split-Path $file -Leaf
|
|
$fileOriginalBackup = "$backupPath\$fileName.original"
|
|
|
|
# 如果原始备份不存在,先创建
|
|
if (-not (Test-Path $fileOriginalBackup)) {
|
|
# 检查当前文件是否已被修改过
|
|
$content = Get-Content $file -Raw -ErrorAction SilentlyContinue
|
|
if ($content -and $content -match "__cursor_patched__") {
|
|
Write-Host "$YELLOW⚠️ [警告]$NC 文件已被修改但无原始备份,将使用当前版本作为基础"
|
|
}
|
|
Copy-Item $file $fileOriginalBackup -Force
|
|
Write-Host "$GREEN✅ [备份]$NC 原始备份创建成功: $fileName"
|
|
} else {
|
|
# 从原始备份恢复,确保每次都是干净的注入
|
|
Write-Host "$BLUE🔄 [恢复]$NC 从原始备份恢复: $fileName"
|
|
Copy-Item $fileOriginalBackup $file -Force
|
|
}
|
|
}
|
|
|
|
# 创建时间戳备份(记录每次修改前的状态)
|
|
foreach ($file in $jsFiles) {
|
|
if (Test-Path $file) {
|
|
$fileName = Split-Path $file -Leaf
|
|
Copy-Item $file "$backupPath\$fileName.backup_$timestamp" -Force
|
|
}
|
|
}
|
|
Write-Host "$GREEN✅ [备份]$NC 时间戳备份创建成功: $backupPath"
|
|
} catch {
|
|
Write-Host "$RED❌ [错误]$NC 创建备份失败: $($_.Exception.Message)"
|
|
return $false
|
|
}
|
|
|
|
# 修改JS文件(每次都重新注入,因为已从原始备份恢复)
|
|
Write-Host "$BLUE🔧 [修改]$NC 开始修改JS文件(使用设备标识符)..."
|
|
|
|
foreach ($file in $jsFiles) {
|
|
if (-not (Test-Path $file)) {
|
|
Write-Host "$YELLOW⚠️ [跳过]$NC 文件不存在: $(Split-Path $file -Leaf)"
|
|
continue
|
|
}
|
|
|
|
Write-Host "$BLUE📝 [处理]$NC 正在处理: $(Split-Path $file -Leaf)"
|
|
|
|
try {
|
|
$content = Get-Content $file -Raw -Encoding UTF8
|
|
$replaced = $false
|
|
$replacedB6 = $false
|
|
|
|
# ========== 方法A: someValue占位符替换(稳定锚点) ==========
|
|
# 这些字符串是固定的占位符,不会被混淆器修改,跨版本稳定
|
|
# 重要说明:
|
|
# 当前 Cursor 的 main.js 中占位符通常是以字符串字面量形式出现,例如:
|
|
# this.machineId="someValue.machineId"
|
|
# 如果直接把 someValue.machineId 替换成 "\"<真实值>\"",会形成 ""<真实值>"" 导致 JS 语法错误(Invalid token)。
|
|
# 因此这里优先替换完整的字符串字面量(包含外层引号),并使用 JSON 字符串字面量确保转义安全。
|
|
|
|
# 🔧 新增: firstSessionDate(重置首次会话日期)
|
|
if (-not $firstSessionDateValue) {
|
|
$firstSessionDateValue = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
|
|
}
|
|
|
|
$placeholders = @(
|
|
@{ Name = 'someValue.machineId'; Value = [string]$machineId },
|
|
@{ Name = 'someValue.macMachineId'; Value = [string]$macMachineId },
|
|
@{ Name = 'someValue.devDeviceId'; Value = [string]$deviceId },
|
|
@{ Name = 'someValue.sqmId'; Value = [string]$sqmId },
|
|
@{ Name = 'someValue.sessionId'; Value = [string]$sessionId },
|
|
@{ Name = 'someValue.firstSessionDate'; Value = [string]$firstSessionDateValue }
|
|
)
|
|
|
|
foreach ($ph in $placeholders) {
|
|
$name = $ph.Name
|
|
$jsonValue = ($ph.Value | ConvertTo-Json -Compress) # 生成带双引号的 JSON 字符串字面量
|
|
|
|
$changed = $false
|
|
|
|
# 优先替换带引号的占位符字面量,避免出现 ""abc"" 破坏语法
|
|
$doubleLiteral = '"' + $name + '"'
|
|
if ($content.Contains($doubleLiteral)) {
|
|
$content = $content.Replace($doubleLiteral, $jsonValue)
|
|
$changed = $true
|
|
}
|
|
$singleLiteral = "'" + $name + "'"
|
|
if ($content.Contains($singleLiteral)) {
|
|
$content = $content.Replace($singleLiteral, $jsonValue)
|
|
$changed = $true
|
|
}
|
|
|
|
# 兜底:如果占位符以非字符串字面量形式出现,则替换为 JSON 字符串字面量(自带引号)
|
|
if (-not $changed -and $content.Contains($name)) {
|
|
$content = $content.Replace($name, $jsonValue)
|
|
$changed = $true
|
|
}
|
|
|
|
if ($changed) {
|
|
Write-Host " $GREEN✓$NC [方案A] 替换 $name"
|
|
$replaced = $true
|
|
}
|
|
}
|
|
|
|
# ========== 方法B: b6 定点重写(机器码源函数,仅 main.js) ==========
|
|
# 说明:b6(t) 是 machineId 的核心生成函数,t=true 返回原始值,t=false 返回哈希
|
|
if ((Split-Path $file -Leaf) -eq "main.js") {
|
|
# ✅ 1+3 融合:限定 out-build/vs/base/node/id.js 模块内做特征匹配 + 花括号配对定位函数边界
|
|
# 目的:提升跨版本覆盖率,同时避免正则跨模块误吞导致 main.js 语法损坏。
|
|
try {
|
|
$moduleMarker = "out-build/vs/base/node/id.js"
|
|
$markerIndex = $content.IndexOf($moduleMarker)
|
|
if ($markerIndex -lt 0) {
|
|
throw "未找到 id.js 模块标记"
|
|
}
|
|
|
|
$windowLen = [Math]::Min($content.Length - $markerIndex, 200000)
|
|
$windowText = $content.Substring($markerIndex, $windowLen)
|
|
|
|
$hashRegex = [regex]::new('createHash\(["'']sha256["'']\)')
|
|
$hashMatches = $hashRegex.Matches($windowText)
|
|
Write-Host " $BLUEℹ️ $NC [方案B诊断] id.js偏移=$markerIndex | sha256 createHash 命中=$($hashMatches.Count)"
|
|
$patched = $false
|
|
$diagLines = @()
|
|
# 兼容:PowerShell 可展开字符串中 "$var:" 会被当作作用域/驱动器前缀解析,需用 "${var}" 明确变量边界
|
|
$candidateNo = 0
|
|
|
|
foreach ($hm in $hashMatches) {
|
|
$candidateNo++
|
|
$hashPos = $hm.Index
|
|
$funcStart = $windowText.LastIndexOf("async function", $hashPos)
|
|
if ($funcStart -lt 0) {
|
|
if ($candidateNo -le 3) { $diagLines += "候选#${candidateNo}: 未找到 async function 起点" }
|
|
continue
|
|
}
|
|
|
|
$openBrace = $windowText.IndexOf("{", $funcStart)
|
|
if ($openBrace -lt 0) {
|
|
if ($candidateNo -le 3) { $diagLines += "候选#${candidateNo}: 未找到函数起始花括号" }
|
|
continue
|
|
}
|
|
|
|
$endBrace = Find-JsMatchingBraceEnd -Text $windowText -OpenBraceIndex $openBrace -MaxScan 20000
|
|
if ($endBrace -lt 0) {
|
|
if ($candidateNo -le 3) { $diagLines += "候选#${candidateNo}: 花括号配对失败(扫描上限内未闭合)" }
|
|
continue
|
|
}
|
|
|
|
$funcText = $windowText.Substring($funcStart, $endBrace - $funcStart + 1)
|
|
if ($funcText.Length -gt 8000) {
|
|
if ($candidateNo -le 3) { $diagLines += "候选#${candidateNo}: 函数体过长 len=$($funcText.Length),已跳过" }
|
|
continue
|
|
}
|
|
|
|
$sig = [regex]::Match($funcText, '^async function (\w+)\((\w+)\)')
|
|
if (-not $sig.Success) {
|
|
if ($candidateNo -le 3) { $diagLines += "候选#${candidateNo}: 未解析到函数签名(async function name(param))" }
|
|
continue
|
|
}
|
|
$fn = $sig.Groups[1].Value
|
|
$param = $sig.Groups[2].Value
|
|
|
|
# 特征校验:sha256 + hex digest + return param ? raw : hash
|
|
$hasDigest = ($funcText -match '\.digest\(["'']hex["'']\)')
|
|
$hasReturn = ($funcText -match ('return\s+' + [regex]::Escape($param) + '\?\w+:\w+\}'))
|
|
if ($candidateNo -le 3) {
|
|
$diagLines += "候选#${candidateNo}: $fn($param) len=$($funcText.Length) digest=$hasDigest return=$hasReturn"
|
|
}
|
|
if (-not $hasDigest) { continue }
|
|
if (-not $hasReturn) { continue }
|
|
|
|
$replacement = "async function $fn($param){return $param?'$machineGuid':'$machineId';}"
|
|
$absStart = $markerIndex + $funcStart
|
|
$absEnd = $markerIndex + $endBrace
|
|
$content = $content.Substring(0, $absStart) + $replacement + $content.Substring($absEnd + 1)
|
|
|
|
Write-Host " $BLUEℹ️ $NC [方案B诊断] 命中候选#${candidateNo}:$fn($param) len=$($funcText.Length)"
|
|
Write-Host " $GREEN✓$NC [方案B] 已重写 $fn($param) 机器码源函数(融合版特征匹配)"
|
|
$replacedB6 = $true
|
|
$patched = $true
|
|
break
|
|
}
|
|
|
|
if (-not $patched) {
|
|
Write-Host " $YELLOW⚠️ $NC [方案B] 未定位到机器码源函数特征,已跳过"
|
|
foreach ($d in ($diagLines | Select-Object -First 3)) {
|
|
Write-Host " $BLUEℹ️ $NC [方案B诊断] $d"
|
|
}
|
|
}
|
|
} catch {
|
|
Write-Host " $YELLOW⚠️ $NC [方案B] 定位失败,已跳过:$($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# ========== 方法C: Loader Stub 注入 ==========
|
|
# 说明:主/共享进程仅注入加载器,具体 Hook 逻辑由外置 cursor_hook.js 维护
|
|
|
|
$injectCode = @"
|
|
// ========== Cursor Hook Loader 开始 ==========
|
|
;(async function(){/*__cursor_patched__*/
|
|
'use strict';
|
|
if (globalThis.__cursor_hook_loaded__) return;
|
|
globalThis.__cursor_hook_loaded__ = true;
|
|
|
|
try {
|
|
// 兼容 ESM/CJS:避免使用 import.meta(仅 ESM 支持),统一用动态 import 加载 Hook
|
|
var fsMod = await import('fs');
|
|
var pathMod = await import('path');
|
|
var osMod = await import('os');
|
|
var urlMod = await import('url');
|
|
|
|
var fs = fsMod && (fsMod.default || fsMod);
|
|
var path = pathMod && (pathMod.default || pathMod);
|
|
var os = osMod && (osMod.default || osMod);
|
|
var url = urlMod && (urlMod.default || urlMod);
|
|
|
|
if (fs && path && os && url && typeof url.pathToFileURL === 'function') {
|
|
var hookPath = path.join(os.homedir(), '.cursor_hook.js');
|
|
if (typeof fs.existsSync === 'function' && fs.existsSync(hookPath)) {
|
|
await import(url.pathToFileURL(hookPath).href);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
// 失败静默,避免影响启动
|
|
}
|
|
})();
|
|
// ========== Cursor Hook Loader 结束 ==========
|
|
|
|
"@
|
|
|
|
# 找到版权声明结束位置并在其后注入(仅注入一次,避免多次插入破坏语法)
|
|
if ($content -match "__cursor_patched__") {
|
|
Write-Host " $YELLOW⚠️ $NC [方案C] 已检测到既有注入标记,跳过重复注入"
|
|
} elseif ($content -match '(\*/\s*\n)') {
|
|
$replacement = '$1' + $injectCode
|
|
$content = [regex]::Replace($content, '(\*/\s*\n)', $replacement, 1)
|
|
Write-Host " $GREEN✓$NC [方案C] Loader Stub 已注入(版权声明后,仅首次)"
|
|
} else {
|
|
# 如果没有找到版权声明,则注入到文件开头
|
|
$content = $injectCode + $content
|
|
Write-Host " $GREEN✓$NC [方案C] Loader Stub 已注入(文件开头)"
|
|
}
|
|
|
|
# 注入一致性校验:避免重复注入导致语法损坏
|
|
$patchedCount = ([regex]::Matches($content, "__cursor_patched__")).Count
|
|
if ($patchedCount -gt 1) {
|
|
throw "检测到重复注入标记:$patchedCount"
|
|
}
|
|
|
|
# 写入修改后的内容
|
|
Set-Content -Path $file -Value $content -Encoding UTF8 -NoNewline
|
|
|
|
# 汇总本次注入实际生效的方案组合
|
|
$summaryParts = @()
|
|
if ($replaced) { $summaryParts += "someValue替换" }
|
|
if ($replacedB6) { $summaryParts += "b6定点重写" }
|
|
$summaryParts += "Hook加载器"
|
|
$summaryText = ($summaryParts -join " + ")
|
|
Write-Host "$GREEN✅ [成功]$NC 增强版方案修改成功($summaryText)"
|
|
$modifiedCount++
|
|
|
|
} catch {
|
|
Write-Host "$RED❌ [错误]$NC 修改文件失败: $($_.Exception.Message)"
|
|
# 尝试从备份恢复
|
|
$fileName = Split-Path $file -Leaf
|
|
$backupFile = "$backupPath\$fileName.original"
|
|
if (Test-Path $backupFile) {
|
|
Copy-Item $backupFile $file -Force
|
|
Write-Host "$YELLOW🔄 [恢复]$NC 已从备份恢复文件"
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($modifiedCount -gt 0) {
|
|
Write-Host ""
|
|
Write-Host "$GREEN🎉 [完成]$NC 成功修改 $modifiedCount 个JS文件"
|
|
Write-Host "$BLUE💾 [备份]$NC 原始文件备份位置: $backupPath"
|
|
Write-Host "$BLUE💡 [说明]$NC 使用增强版三重方案:"
|
|
Write-Host " • 方案A: someValue占位符替换(稳定锚点,跨版本兼容)"
|
|
Write-Host " • 方案B: b6 定点重写(机器码源函数)"
|
|
Write-Host " • 方案C: Loader Stub + 外置 Hook(cursor_hook.js)"
|
|
Write-Host "$BLUE📁 [配置]$NC ID 配置文件: $idsConfigPath"
|
|
return $true
|
|
} else {
|
|
Write-Host "$RED❌ [失败]$NC 没有成功修改任何文件"
|
|
return $false
|
|
}
|
|
}
|
|
|
|
|
|
# 🚀 新增 Cursor 防掉试用Pro删除文件夹功能
|
|
function Remove-CursorTrialFolders {
|
|
Write-Host ""
|
|
Write-Host "$GREEN🎯 [核心功能]$NC 正在执行 Cursor 防掉试用Pro删除文件夹..."
|
|
Write-Host "$BLUE📋 [说明]$NC 此功能将删除指定的Cursor相关文件夹以重置试用状态"
|
|
Write-Host ""
|
|
|
|
# 定义需要删除的文件夹路径
|
|
$foldersToDelete = @()
|
|
|
|
# Windows Administrator 用户路径
|
|
$adminPaths = @(
|
|
"C:\Users\Administrator\.cursor",
|
|
"C:\Users\Administrator\AppData\Roaming\Cursor"
|
|
)
|
|
|
|
# 当前用户路径(使用解析后的用户目录和 AppData)
|
|
$currentUserPaths = @()
|
|
$userProfileRoot = if ($global:CursorUserProfileRoot) { $global:CursorUserProfileRoot } else { [Environment]::GetEnvironmentVariable("USERPROFILE") }
|
|
if ($userProfileRoot) {
|
|
$currentUserPaths += (Join-Path $userProfileRoot ".cursor")
|
|
}
|
|
if ($global:CursorAppDataDir) {
|
|
$currentUserPaths += $global:CursorAppDataDir
|
|
}
|
|
|
|
# 合并所有路径
|
|
$foldersToDelete += $adminPaths
|
|
$foldersToDelete += $currentUserPaths
|
|
|
|
Write-Host "$BLUE📂 [检测]$NC 将检查以下文件夹:"
|
|
foreach ($folder in $foldersToDelete) {
|
|
Write-Host " 📁 $folder"
|
|
}
|
|
Write-Host ""
|
|
|
|
$deletedCount = 0
|
|
$skippedCount = 0
|
|
$errorCount = 0
|
|
|
|
# 删除指定文件夹
|
|
foreach ($folder in $foldersToDelete) {
|
|
Write-Host "$BLUE🔍 [检查]$NC 检查文件夹: $folder"
|
|
|
|
if (Test-Path $folder) {
|
|
try {
|
|
Write-Host "$YELLOW⚠️ [警告]$NC 发现文件夹存在,正在删除..."
|
|
Remove-Item -Path $folder -Recurse -Force -ErrorAction Stop
|
|
Write-Host "$GREEN✅ [成功]$NC 已删除文件夹: $folder"
|
|
$deletedCount++
|
|
}
|
|
catch {
|
|
Write-Host "$RED❌ [错误]$NC 删除文件夹失败: $folder"
|
|
Write-Host "$RED💥 [详情]$NC 错误信息: $($_.Exception.Message)"
|
|
$errorCount++
|
|
}
|
|
} else {
|
|
Write-Host "$YELLOW⏭️ [跳过]$NC 文件夹不存在: $folder"
|
|
$skippedCount++
|
|
}
|
|
Write-Host ""
|
|
}
|
|
|
|
# 显示操作统计
|
|
Write-Host "$GREEN📊 [统计]$NC 操作完成统计:"
|
|
Write-Host " ✅ 成功删除: $deletedCount 个文件夹"
|
|
Write-Host " ⏭️ 跳过处理: $skippedCount 个文件夹"
|
|
Write-Host " ❌ 删除失败: $errorCount 个文件夹"
|
|
Write-Host ""
|
|
|
|
if ($deletedCount -gt 0) {
|
|
Write-Host "$GREEN🎉 [完成]$NC Cursor 防掉试用Pro文件夹删除完成!"
|
|
|
|
# 🔧 预创建必要的目录结构,避免权限问题
|
|
Write-Host "$BLUE🔧 [修复]$NC 预创建必要的目录结构以避免权限问题..."
|
|
|
|
$cursorAppData = $global:CursorAppDataDir
|
|
$cursorLocalAppData = $global:CursorLocalAppDataDir
|
|
$cursorUserProfile = if ($userProfileRoot) { Join-Path $userProfileRoot ".cursor" } else { "$env:USERPROFILE\.cursor" }
|
|
|
|
# 创建主要目录
|
|
try {
|
|
if ($cursorAppData -and -not (Test-Path $cursorAppData)) {
|
|
New-Item -ItemType Directory -Path $cursorAppData -Force | Out-Null
|
|
}
|
|
if ($cursorUserProfile -and -not (Test-Path $cursorUserProfile)) {
|
|
New-Item -ItemType Directory -Path $cursorUserProfile -Force | Out-Null
|
|
}
|
|
Write-Host "$GREEN✅ [完成]$NC 目录结构预创建完成"
|
|
} catch {
|
|
Write-Host "$YELLOW⚠️ [警告]$NC 预创建目录时出现问题: $($_.Exception.Message)"
|
|
}
|
|
} else {
|
|
Write-Host "$YELLOW🤔 [提示]$NC 未找到需要删除的文件夹,可能已经清理过了"
|
|
}
|
|
Write-Host ""
|
|
}
|
|
|
|
# 🔄 重启Cursor并等待配置文件生成
|
|
function Restart-CursorAndWait {
|
|
Write-Host ""
|
|
Write-Host "$GREEN🔄 [重启]$NC 正在重启Cursor以重新生成配置文件..."
|
|
|
|
if (-not $global:CursorProcessInfo) {
|
|
Write-Host "$RED❌ [错误]$NC 未找到Cursor进程信息,无法重启"
|
|
return $false
|
|
}
|
|
|
|
$cursorPath = $global:CursorProcessInfo.Path
|
|
|
|
# 修复:确保路径是字符串类型
|
|
if ($cursorPath -is [array]) {
|
|
$cursorPath = $cursorPath[0]
|
|
}
|
|
|
|
# 验证路径不为空
|
|
if ([string]::IsNullOrEmpty($cursorPath)) {
|
|
Write-Host "$RED❌ [错误]$NC Cursor路径为空"
|
|
return $false
|
|
}
|
|
|
|
Write-Host "$BLUE📍 [路径]$NC 使用路径: $cursorPath"
|
|
|
|
if (-not (Test-Path $cursorPath)) {
|
|
Write-Host "$RED❌ [错误]$NC Cursor可执行文件不存在: $cursorPath"
|
|
|
|
# 尝试重新解析安装路径
|
|
$installPath = Resolve-CursorInstallPath -AllowPrompt
|
|
$foundPath = if ($installPath) { Join-Path $installPath "Cursor.exe" } else { $null }
|
|
if ($foundPath -and (Test-Path $foundPath)) {
|
|
Write-Host "$GREEN💡 [发现]$NC 使用备用路径: $foundPath"
|
|
} else {
|
|
$foundPath = $null
|
|
}
|
|
|
|
if (-not $foundPath) {
|
|
Write-Host "$RED❌ [错误]$NC 无法找到有效的Cursor可执行文件"
|
|
return $false
|
|
}
|
|
|
|
$cursorPath = $foundPath
|
|
}
|
|
|
|
try {
|
|
Write-Host "$GREEN🚀 [启动]$NC 正在启动Cursor..."
|
|
$process = Start-Process -FilePath $cursorPath -PassThru -WindowStyle Hidden
|
|
|
|
Write-Host "$YELLOW⏳ [等待]$NC 等待20秒让Cursor完全启动并生成配置文件..."
|
|
Start-Sleep -Seconds 20
|
|
|
|
# 检查配置文件是否生成
|
|
$configPath = $STORAGE_FILE
|
|
if (-not $configPath) {
|
|
Write-Host "$RED❌ [错误]$NC 无法解析配置文件路径"
|
|
return $false
|
|
}
|
|
$maxWait = 45
|
|
$waited = 0
|
|
|
|
while (-not (Test-Path $configPath) -and $waited -lt $maxWait) {
|
|
Write-Host "$YELLOW⏳ [等待]$NC 等待配置文件生成... ($waited/$maxWait 秒)"
|
|
Start-Sleep -Seconds 1
|
|
$waited++
|
|
}
|
|
|
|
if (Test-Path $configPath) {
|
|
Write-Host "$GREEN✅ [成功]$NC 配置文件已生成: $configPath"
|
|
|
|
# 额外等待确保文件完全写入
|
|
Write-Host "$YELLOW⏳ [等待]$NC 等待5秒确保配置文件完全写入..."
|
|
Start-Sleep -Seconds 5
|
|
} else {
|
|
Write-Host "$YELLOW⚠️ [警告]$NC 配置文件未在预期时间内生成"
|
|
Write-Host "$BLUE💡 [提示]$NC 可能需要手动启动Cursor一次来生成配置文件"
|
|
}
|
|
|
|
# 强制关闭Cursor
|
|
Write-Host "$YELLOW🔄 [关闭]$NC 正在关闭Cursor以进行配置修改..."
|
|
if ($process -and -not $process.HasExited) {
|
|
$process.Kill()
|
|
$process.WaitForExit(5000)
|
|
}
|
|
|
|
# 确保所有Cursor进程都关闭
|
|
Get-Process -Name "Cursor" -ErrorAction SilentlyContinue | Stop-Process -Force
|
|
Get-Process -Name "cursor" -ErrorAction SilentlyContinue | Stop-Process -Force
|
|
|
|
Write-Host "$GREEN✅ [完成]$NC Cursor重启流程完成"
|
|
return $true
|
|
|
|
} catch {
|
|
Write-Host "$RED❌ [错误]$NC 重启Cursor失败: $($_.Exception.Message)"
|
|
Write-Host "$BLUE💡 [调试]$NC 错误详情: $($_.Exception.GetType().FullName)"
|
|
return $false
|
|
}
|
|
}
|
|
|
|
# 🔒 强制关闭所有Cursor进程(增强版)
|
|
function Stop-AllCursorProcesses {
|
|
param(
|
|
[int]$MaxRetries = 3,
|
|
[int]$WaitSeconds = 5
|
|
)
|
|
|
|
Write-Host "$BLUE🔒 [进程检查]$NC 正在检查并关闭所有Cursor相关进程..."
|
|
|
|
# 定义所有可能的Cursor进程名称
|
|
$cursorProcessNames = @(
|
|
"Cursor",
|
|
"cursor",
|
|
"Cursor Helper",
|
|
"Cursor Helper (GPU)",
|
|
"Cursor Helper (Plugin)",
|
|
"Cursor Helper (Renderer)",
|
|
"CursorUpdater"
|
|
)
|
|
|
|
for ($retry = 1; $retry -le $MaxRetries; $retry++) {
|
|
Write-Host "$BLUE🔍 [检查]$NC 第 $retry/$MaxRetries 次进程检查..."
|
|
|
|
$foundProcesses = @()
|
|
foreach ($processName in $cursorProcessNames) {
|
|
$processes = Get-Process -Name $processName -ErrorAction SilentlyContinue
|
|
if ($processes) {
|
|
$foundProcesses += $processes
|
|
Write-Host "$YELLOW⚠️ [发现]$NC 进程: $processName (PID: $($processes.Id -join ', '))"
|
|
}
|
|
}
|
|
|
|
if ($foundProcesses.Count -eq 0) {
|
|
Write-Host "$GREEN✅ [成功]$NC 所有Cursor进程已关闭"
|
|
return $true
|
|
}
|
|
|
|
Write-Host "$YELLOW🔄 [关闭]$NC 正在关闭 $($foundProcesses.Count) 个Cursor进程..."
|
|
|
|
# 先尝试优雅关闭
|
|
foreach ($process in $foundProcesses) {
|
|
try {
|
|
$process.CloseMainWindow() | Out-Null
|
|
Write-Host "$BLUE • 优雅关闭: $($process.ProcessName) (PID: $($process.Id))$NC"
|
|
} catch {
|
|
Write-Host "$YELLOW • 优雅关闭失败: $($process.ProcessName)$NC"
|
|
}
|
|
}
|
|
|
|
Start-Sleep -Seconds 3
|
|
|
|
# 强制终止仍在运行的进程
|
|
foreach ($processName in $cursorProcessNames) {
|
|
$processes = Get-Process -Name $processName -ErrorAction SilentlyContinue
|
|
if ($processes) {
|
|
foreach ($process in $processes) {
|
|
try {
|
|
Stop-Process -Id $process.Id -Force
|
|
Write-Host "$RED • 强制终止: $($process.ProcessName) (PID: $($process.Id))$NC"
|
|
} catch {
|
|
Write-Host "$RED • 强制终止失败: $($process.ProcessName)$NC"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($retry -lt $MaxRetries) {
|
|
Write-Host "$YELLOW⏳ [等待]$NC 等待 $WaitSeconds 秒后重新检查..."
|
|
Start-Sleep -Seconds $WaitSeconds
|
|
}
|
|
}
|
|
|
|
Write-Host "$RED❌ [失败]$NC 经过 $MaxRetries 次尝试仍有Cursor进程在运行"
|
|
return $false
|
|
}
|
|
|
|
# 🔐 检查文件权限和锁定状态
|
|
function Test-FileAccessibility {
|
|
param(
|
|
[string]$FilePath
|
|
)
|
|
|
|
Write-Host "$BLUE🔐 [权限检查]$NC 检查文件访问权限: $(Split-Path $FilePath -Leaf)"
|
|
|
|
if (-not (Test-Path $FilePath)) {
|
|
Write-Host "$RED❌ [错误]$NC 文件不存在"
|
|
return $false
|
|
}
|
|
|
|
# 检查文件是否被锁定
|
|
try {
|
|
$fileStream = [System.IO.File]::Open($FilePath, 'Open', 'ReadWrite', 'None')
|
|
$fileStream.Close()
|
|
Write-Host "$GREEN✅ [权限]$NC 文件可读写,无锁定"
|
|
return $true
|
|
} catch [System.IO.IOException] {
|
|
Write-Host "$RED❌ [锁定]$NC 文件被其他进程锁定: $($_.Exception.Message)"
|
|
return $false
|
|
} catch [System.UnauthorizedAccessException] {
|
|
Write-Host "$YELLOW⚠️ [权限]$NC 文件权限受限,尝试修改权限..."
|
|
|
|
# 尝试修改文件权限
|
|
try {
|
|
$file = Get-Item $FilePath
|
|
if ($file.IsReadOnly) {
|
|
$file.IsReadOnly = $false
|
|
Write-Host "$GREEN✅ [修复]$NC 已移除只读属性"
|
|
}
|
|
|
|
# 再次测试
|
|
$fileStream = [System.IO.File]::Open($FilePath, 'Open', 'ReadWrite', 'None')
|
|
$fileStream.Close()
|
|
Write-Host "$GREEN✅ [权限]$NC 权限修复成功"
|
|
return $true
|
|
} catch {
|
|
Write-Host "$RED❌ [权限]$NC 无法修复权限: $($_.Exception.Message)"
|
|
return $false
|
|
}
|
|
} catch {
|
|
Write-Host "$RED❌ [错误]$NC 未知错误: $($_.Exception.Message)"
|
|
return $false
|
|
}
|
|
}
|
|
|
|
# 🧹 Cursor 初始化清理功能(从旧版本移植)
|
|
function Invoke-CursorInitialization {
|
|
Write-Host ""
|
|
Write-Host "$GREEN🧹 [初始化]$NC 正在执行 Cursor 初始化清理..."
|
|
$BASE_PATH = if ($global:CursorAppDataDir) { Join-Path $global:CursorAppDataDir "User" } else { $null }
|
|
if (-not $BASE_PATH) {
|
|
Write-Host "$RED❌ [错误]$NC 无法解析 Cursor 用户目录,初始化清理终止"
|
|
return
|
|
}
|
|
|
|
$filesToDelete = @(
|
|
(Join-Path -Path $BASE_PATH -ChildPath "globalStorage\state.vscdb"),
|
|
(Join-Path -Path $BASE_PATH -ChildPath "globalStorage\state.vscdb.backup")
|
|
)
|
|
|
|
$folderToCleanContents = Join-Path -Path $BASE_PATH -ChildPath "History"
|
|
$folderToDeleteCompletely = Join-Path -Path $BASE_PATH -ChildPath "workspaceStorage"
|
|
|
|
Write-Host "$BLUE🔍 [调试]$NC 基础路径: $BASE_PATH"
|
|
|
|
# 删除指定文件
|
|
foreach ($file in $filesToDelete) {
|
|
Write-Host "$BLUE🔍 [检查]$NC 检查文件: $file"
|
|
if (Test-Path $file) {
|
|
try {
|
|
Remove-Item -Path $file -Force -ErrorAction Stop
|
|
Write-Host "$GREEN✅ [成功]$NC 已删除文件: $file"
|
|
}
|
|
catch {
|
|
Write-Host "$RED❌ [错误]$NC 删除文件 $file 失败: $($_.Exception.Message)"
|
|
}
|
|
} else {
|
|
Write-Host "$YELLOW⚠️ [跳过]$NC 文件不存在,跳过删除: $file"
|
|
}
|
|
}
|
|
|
|
# 清空指定文件夹内容
|
|
Write-Host "$BLUE🔍 [检查]$NC 检查待清空文件夹: $folderToCleanContents"
|
|
if (Test-Path $folderToCleanContents) {
|
|
try {
|
|
Get-ChildItem -Path $folderToCleanContents -Recurse | Remove-Item -Force -Recurse -ErrorAction Stop
|
|
Write-Host "$GREEN✅ [成功]$NC 已清空文件夹内容: $folderToCleanContents"
|
|
}
|
|
catch {
|
|
Write-Host "$RED❌ [错误]$NC 清空文件夹 $folderToCleanContents 失败: $($_.Exception.Message)"
|
|
}
|
|
} else {
|
|
Write-Host "$YELLOW⚠️ [跳过]$NC 文件夹不存在,跳过清空: $folderToCleanContents"
|
|
}
|
|
|
|
# 完全删除指定文件夹
|
|
Write-Host "$BLUE🔍 [检查]$NC 检查待删除文件夹: $folderToDeleteCompletely"
|
|
if (Test-Path $folderToDeleteCompletely) {
|
|
try {
|
|
Remove-Item -Path $folderToDeleteCompletely -Recurse -Force -ErrorAction Stop
|
|
Write-Host "$GREEN✅ [成功]$NC 已删除文件夹: $folderToDeleteCompletely"
|
|
}
|
|
catch {
|
|
Write-Host "$RED❌ [错误]$NC 删除文件夹 $folderToDeleteCompletely 失败: $($_.Exception.Message)"
|
|
}
|
|
} else {
|
|
Write-Host "$YELLOW⚠️ [跳过]$NC 文件夹不存在,跳过删除: $folderToDeleteCompletely"
|
|
}
|
|
|
|
Write-Host "$GREEN✅ [完成]$NC Cursor 初始化清理完成"
|
|
Write-Host ""
|
|
}
|
|
|
|
# 🔧 修改系统注册表 MachineGuid(从旧版本移植)
|
|
function Update-MachineGuid {
|
|
try {
|
|
Write-Host "$BLUE🔧 [注册表]$NC 正在修改系统注册表 MachineGuid..."
|
|
|
|
# 检查注册表路径是否存在,不存在则创建
|
|
$registryPath = "HKLM:\SOFTWARE\Microsoft\Cryptography"
|
|
if (-not (Test-Path $registryPath)) {
|
|
Write-Host "$YELLOW⚠️ [警告]$NC 注册表路径不存在: $registryPath,正在创建..."
|
|
New-Item -Path $registryPath -Force | Out-Null
|
|
Write-Host "$GREEN✅ [信息]$NC 注册表路径创建成功"
|
|
}
|
|
|
|
# 获取当前的 MachineGuid,如果不存在则使用空字符串作为默认值
|
|
$originalGuid = ""
|
|
try {
|
|
$currentGuid = Get-ItemProperty -Path $registryPath -Name MachineGuid -ErrorAction SilentlyContinue
|
|
if ($currentGuid) {
|
|
$originalGuid = $currentGuid.MachineGuid
|
|
Write-Host "$GREEN✅ [信息]$NC 当前注册表值:"
|
|
Write-Host "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography"
|
|
Write-Host " MachineGuid REG_SZ $originalGuid"
|
|
} else {
|
|
Write-Host "$YELLOW⚠️ [警告]$NC MachineGuid 值不存在,将创建新值"
|
|
}
|
|
} catch {
|
|
Write-Host "$YELLOW⚠️ [警告]$NC 读取注册表失败: $($_.Exception.Message)"
|
|
Write-Host "$YELLOW⚠️ [警告]$NC 将尝试创建新的 MachineGuid 值"
|
|
}
|
|
|
|
# 创建备份文件(仅当原始值存在时)
|
|
$backupFile = $null
|
|
if ($originalGuid) {
|
|
$backupFile = "$BACKUP_DIR\MachineGuid_$(Get-Date -Format 'yyyyMMdd_HHmmss').reg"
|
|
Write-Host "$BLUE💾 [备份]$NC 正在备份注册表..."
|
|
$backupResult = Start-Process "reg.exe" -ArgumentList "export", "`"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography`"", "`"$backupFile`"" -NoNewWindow -Wait -PassThru
|
|
|
|
if ($backupResult.ExitCode -eq 0) {
|
|
Write-Host "$GREEN✅ [备份]$NC 注册表项已备份到:$backupFile"
|
|
} else {
|
|
Write-Host "$YELLOW⚠️ [警告]$NC 备份创建失败,继续执行..."
|
|
$backupFile = $null
|
|
}
|
|
}
|
|
|
|
# 生成新GUID
|
|
$newGuid = [System.Guid]::NewGuid().ToString()
|
|
Write-Host "$BLUE🔄 [生成]$NC 新的 MachineGuid: $newGuid"
|
|
|
|
# 更新或创建注册表值
|
|
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 注册表操作失败:$($_.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
|
|
}
|
|
}
|
|
|
|
# 🚫 禁用 Cursor 自动更新(Windows)
|
|
function Disable-CursorAutoUpdate {
|
|
Write-Host ""
|
|
Write-Host "$BLUE🚫 [禁用更新]$NC 正在尝试禁用 Cursor 自动更新..."
|
|
|
|
# 检测 Cursor 安装路径(支持自动检测 + 手动兜底)
|
|
$cursorAppPath = Resolve-CursorInstallPath -AllowPrompt
|
|
if (-not $cursorAppPath) {
|
|
Write-Host "$YELLOW⚠️ [警告]$NC 未找到 Cursor 安装路径,跳过禁用更新"
|
|
return $false
|
|
}
|
|
|
|
# 更新配置文件(JSON/YAML)
|
|
# 兼容修复:PowerShell 不支持把 (if ... ) 当作表达式写进数组里,会报 “if 不是 cmdlet”
|
|
$updateFiles = @()
|
|
$updateFiles += "$cursorAppPath\resources\app-update.yml"
|
|
$updateFiles += "$cursorAppPath\resources\app\update-config.json"
|
|
if ($global:CursorAppDataDir) {
|
|
$updateFiles += (Join-Path $global:CursorAppDataDir "update-config.json")
|
|
$updateFiles += (Join-Path $global:CursorAppDataDir "settings.json")
|
|
}
|
|
$updateFiles = $updateFiles | Where-Object { $_ }
|
|
|
|
foreach ($file in $updateFiles) {
|
|
if (-not (Test-Path $file)) { continue }
|
|
|
|
try {
|
|
Copy-Item $file "$file.bak_$(Get-Date -Format 'yyyyMMdd_HHmmss')" -Force
|
|
} catch {
|
|
Write-Host "$YELLOW⚠️ [警告]$NC 备份失败: $file"
|
|
}
|
|
|
|
if ($file -like "*.yml") {
|
|
Set-Content -Path $file -Value "# update disabled by script $(Get-Date)" -Encoding UTF8
|
|
Write-Host "$GREEN✅ [完成]$NC 已处理更新配置: $file"
|
|
continue
|
|
}
|
|
|
|
if ($file -like "*update-config.json") {
|
|
$config = @{ autoCheck = $false; autoDownload = $false }
|
|
$config | ConvertTo-Json -Depth 5 | Set-Content -Path $file -Encoding UTF8
|
|
Write-Host "$GREEN✅ [完成]$NC 已处理更新配置: $file"
|
|
continue
|
|
}
|
|
|
|
if ($file -like "*settings.json") {
|
|
try {
|
|
$settings = Get-Content $file -Raw -Encoding UTF8 | ConvertFrom-Json -ErrorAction Stop
|
|
} catch {
|
|
$settings = @{}
|
|
}
|
|
if ($settings -is [hashtable]) {
|
|
$settings["update.mode"] = "none"
|
|
} else {
|
|
$settings | Add-Member -MemberType NoteProperty -Name "update.mode" -Value "none" -Force
|
|
}
|
|
$settings | ConvertTo-Json -Depth 10 | Set-Content -Path $file -Encoding UTF8
|
|
Write-Host "$GREEN✅ [完成]$NC 已处理更新配置: $file"
|
|
continue
|
|
}
|
|
}
|
|
|
|
# 尝试禁用更新器可执行文件
|
|
$updaterCandidates = @()
|
|
$updaterCandidates += "$cursorAppPath\Update.exe"
|
|
if ($global:CursorLocalAppDataDir) {
|
|
$updaterCandidates += (Join-Path $global:CursorLocalAppDataDir "Update.exe")
|
|
}
|
|
$updaterCandidates += "$cursorAppPath\CursorUpdater.exe"
|
|
$updaterCandidates = $updaterCandidates | Where-Object { $_ }
|
|
|
|
foreach ($updater in $updaterCandidates) {
|
|
if (-not (Test-Path $updater)) { continue }
|
|
$backup = "$updater.bak_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
|
|
try {
|
|
Move-Item -Path $updater -Destination $backup -Force
|
|
Write-Host "$GREEN✅ [完成]$NC 已禁用更新器: $updater"
|
|
} catch {
|
|
Write-Host "$YELLOW⚠️ [警告]$NC 更新器禁用失败: $updater"
|
|
}
|
|
}
|
|
|
|
return $true
|
|
}
|
|
|
|
# 检查配置文件和环境
|
|
function Test-CursorEnvironment {
|
|
param(
|
|
[string]$Mode = "FULL"
|
|
)
|
|
|
|
Write-Host ""
|
|
Write-Host "$BLUE🔍 [环境检查]$NC 正在检查Cursor环境..."
|
|
|
|
$configPath = $STORAGE_FILE
|
|
$cursorAppData = $global:CursorAppDataDir
|
|
$issues = @()
|
|
|
|
# 检查配置文件
|
|
if (-not $configPath) {
|
|
$issues += "无法解析配置文件路径"
|
|
} elseif (-not (Test-Path $configPath)) {
|
|
$issues += "配置文件不存在: $configPath"
|
|
} else {
|
|
try {
|
|
$content = Get-Content $configPath -Raw -Encoding UTF8 -ErrorAction Stop
|
|
$config = $content | ConvertFrom-Json -ErrorAction Stop
|
|
Write-Host "$GREEN✅ [检查]$NC 配置文件格式正确"
|
|
} catch {
|
|
$issues += "配置文件格式错误: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# 检查Cursor目录结构
|
|
if (-not $cursorAppData -or -not (Test-Path $cursorAppData)) {
|
|
$issues += "Cursor应用数据目录不存在: $cursorAppData"
|
|
}
|
|
|
|
# 检查Cursor安装
|
|
$cursorPaths = @()
|
|
$installPath = Resolve-CursorInstallPath
|
|
if ($installPath) {
|
|
$cursorPaths = @(Join-Path $installPath "Cursor.exe")
|
|
}
|
|
|
|
$cursorFound = $false
|
|
foreach ($path in $cursorPaths) {
|
|
if (Test-Path $path) {
|
|
Write-Host "$GREEN✅ [检查]$NC 找到Cursor安装: $path"
|
|
$cursorFound = $true
|
|
break
|
|
}
|
|
}
|
|
|
|
if (-not $cursorFound) {
|
|
$issues += "未找到Cursor安装,请确认Cursor已正确安装"
|
|
}
|
|
|
|
# 返回检查结果
|
|
if ($issues.Count -eq 0) {
|
|
Write-Host "$GREEN✅ [环境检查]$NC 所有检查通过"
|
|
return @{ Success = $true; Issues = @() }
|
|
} else {
|
|
Write-Host "$RED❌ [环境检查]$NC 发现 $($issues.Count) 个问题:"
|
|
foreach ($issue in $issues) {
|
|
Write-Host "$RED • ${issue}$NC"
|
|
}
|
|
return @{ Success = $false; Issues = $issues }
|
|
}
|
|
}
|
|
|
|
# �🛠️ 修改机器码配置(增强版)
|
|
function Modify-MachineCodeConfig {
|
|
param(
|
|
[string]$Mode = "FULL"
|
|
)
|
|
|
|
Write-Host ""
|
|
Write-Host "$GREEN🛠️ [配置]$NC 正在修改机器码配置..."
|
|
|
|
$configPath = $STORAGE_FILE
|
|
if (-not $configPath) {
|
|
Write-Host "$RED❌ [错误]$NC 无法解析配置文件路径"
|
|
return $false
|
|
}
|
|
|
|
# 增强的配置文件检查
|
|
if (-not (Test-Path $configPath)) {
|
|
Write-Host "$RED❌ [错误]$NC 配置文件不存在: $configPath"
|
|
Write-Host ""
|
|
Write-Host "$YELLOW💡 [解决方案]$NC 请尝试以下步骤:"
|
|
Write-Host "$BLUE 1️⃣ 手动启动Cursor应用程序$NC"
|
|
Write-Host "$BLUE 2️⃣ 等待Cursor完全加载(约30秒)$NC"
|
|
Write-Host "$BLUE 3️⃣ 关闭Cursor应用程序$NC"
|
|
Write-Host "$BLUE 4️⃣ 重新运行此脚本$NC"
|
|
Write-Host ""
|
|
Write-Host "$YELLOW⚠️ [备选方案]$NC 如果问题持续:"
|
|
Write-Host "$BLUE • 选择脚本的'重置环境+修改机器码'选项$NC"
|
|
Write-Host "$BLUE • 该选项会自动生成配置文件$NC"
|
|
Write-Host ""
|
|
|
|
# 提供用户选择
|
|
$userChoice = Read-Host "是否现在尝试启动Cursor生成配置文件?(y/n)"
|
|
if ($userChoice -match "^(y|yes)$") {
|
|
Write-Host "$BLUE🚀 [尝试]$NC 正在尝试启动Cursor..."
|
|
return Start-CursorToGenerateConfig
|
|
}
|
|
|
|
return $false
|
|
}
|
|
|
|
# 在仅修改机器码模式下也要确保进程完全关闭
|
|
if ($Mode -eq "MODIFY_ONLY") {
|
|
Write-Host "$BLUE🔒 [安全检查]$NC 即使在仅修改模式下,也需要确保Cursor进程完全关闭"
|
|
if (-not (Stop-AllCursorProcesses -MaxRetries 3 -WaitSeconds 3)) {
|
|
Write-Host "$RED❌ [错误]$NC 无法关闭所有Cursor进程,修改可能失败"
|
|
$userChoice = Read-Host "是否强制继续?(y/n)"
|
|
if ($userChoice -notmatch "^(y|yes)$") {
|
|
return $false
|
|
}
|
|
}
|
|
}
|
|
|
|
# 检查文件权限和锁定状态
|
|
if (-not (Test-FileAccessibility -FilePath $configPath)) {
|
|
Write-Host "$RED❌ [错误]$NC 无法访问配置文件,可能被锁定或权限不足"
|
|
return $false
|
|
}
|
|
|
|
# 验证配置文件格式并显示结构
|
|
try {
|
|
Write-Host "$BLUE🔍 [验证]$NC 检查配置文件格式..."
|
|
$originalContent = Get-Content $configPath -Raw -Encoding UTF8 -ErrorAction Stop
|
|
$config = $originalContent | ConvertFrom-Json -ErrorAction Stop
|
|
Write-Host "$GREEN✅ [验证]$NC 配置文件格式正确"
|
|
|
|
# 显示当前配置文件中的相关属性
|
|
Write-Host "$BLUE📋 [当前配置]$NC 检查现有的遥测属性:"
|
|
$telemetryProperties = @('telemetry.machineId', 'telemetry.macMachineId', 'telemetry.devDeviceId', 'telemetry.sqmId')
|
|
foreach ($prop in $telemetryProperties) {
|
|
if ($config.PSObject.Properties[$prop]) {
|
|
$value = $config.$prop
|
|
$displayValue = if ($value.Length -gt 20) { "$($value.Substring(0,20))..." } else { $value }
|
|
Write-Host "$GREEN ✓ ${prop}$NC = $displayValue"
|
|
} else {
|
|
Write-Host "$YELLOW - ${prop}$NC (不存在,将创建)"
|
|
}
|
|
}
|
|
Write-Host ""
|
|
} catch {
|
|
Write-Host "$RED❌ [错误]$NC 配置文件格式错误: $($_.Exception.Message)"
|
|
Write-Host "$YELLOW💡 [建议]$NC 配置文件可能已损坏,建议选择'重置环境+修改机器码'选项"
|
|
return $false
|
|
}
|
|
|
|
# 实现原子性文件操作和重试机制
|
|
$maxRetries = 3
|
|
$retryCount = 0
|
|
|
|
while ($retryCount -lt $maxRetries) {
|
|
$retryCount++
|
|
Write-Host ""
|
|
Write-Host "$BLUE🔄 [尝试]$NC 第 $retryCount/$maxRetries 次修改尝试..."
|
|
|
|
try {
|
|
# 显示操作进度
|
|
Write-Host "$BLUE⏳ [进度]$NC 1/6 - 生成新的设备标识符..."
|
|
|
|
# 生成新的ID
|
|
$MAC_MACHINE_ID = [System.Guid]::NewGuid().ToString()
|
|
$UUID = [System.Guid]::NewGuid().ToString()
|
|
$prefixBytes = [System.Text.Encoding]::UTF8.GetBytes("auth0|user_")
|
|
$prefixHex = -join ($prefixBytes | ForEach-Object { '{0:x2}' -f $_ })
|
|
$randomBytes = New-Object byte[] 32
|
|
$rng = [System.Security.Cryptography.RNGCryptoServiceProvider]::new()
|
|
$rng.GetBytes($randomBytes)
|
|
$randomPart = [System.BitConverter]::ToString($randomBytes) -replace '-',''
|
|
$rng.Dispose()
|
|
$MACHINE_ID = "${prefixHex}${randomPart}"
|
|
$SQM_ID = "{$([System.Guid]::NewGuid().ToString().ToUpper())}"
|
|
# 🔧 新增: serviceMachineId (用于 storage.serviceMachineId)
|
|
$SERVICE_MACHINE_ID = [System.Guid]::NewGuid().ToString()
|
|
# 🔧 新增: firstSessionDate (重置首次会话日期)
|
|
$FIRST_SESSION_DATE = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
|
|
$SESSION_ID = [System.Guid]::NewGuid().ToString()
|
|
|
|
# 共享ID(用于配置与JS注入保持一致)
|
|
$global:CursorIds = @{
|
|
machineId = $MACHINE_ID
|
|
macMachineId = $MAC_MACHINE_ID
|
|
devDeviceId = $UUID
|
|
sqmId = $SQM_ID
|
|
firstSessionDate = $FIRST_SESSION_DATE
|
|
sessionId = $SESSION_ID
|
|
macAddress = "00:11:22:33:44:55"
|
|
}
|
|
|
|
Write-Host "$GREEN✅ [进度]$NC 1/7 - 设备标识符生成完成"
|
|
|
|
Write-Host "$BLUE⏳ [进度]$NC 2/7 - 创建备份目录..."
|
|
|
|
# 备份原始值(增强版)
|
|
$backupDir = $BACKUP_DIR
|
|
if (-not $backupDir) {
|
|
throw "无法解析备份目录路径"
|
|
}
|
|
if (-not (Test-Path $backupDir)) {
|
|
New-Item -ItemType Directory -Path $backupDir -Force -ErrorAction Stop | Out-Null
|
|
}
|
|
|
|
$backupName = "storage.json.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')_retry$retryCount"
|
|
$backupPath = "$backupDir\$backupName"
|
|
|
|
Write-Host "$BLUE⏳ [进度]$NC 3/7 - 备份原始配置..."
|
|
Copy-Item $configPath $backupPath -ErrorAction Stop
|
|
|
|
# 验证备份是否成功
|
|
if (Test-Path $backupPath) {
|
|
$backupSize = (Get-Item $backupPath).Length
|
|
$originalSize = (Get-Item $configPath).Length
|
|
if ($backupSize -eq $originalSize) {
|
|
Write-Host "$GREEN✅ [进度]$NC 3/7 - 配置备份成功: $backupName"
|
|
} else {
|
|
Write-Host "$YELLOW⚠️ [警告]$NC 备份文件大小不匹配,但继续执行"
|
|
}
|
|
} else {
|
|
throw "备份文件创建失败"
|
|
}
|
|
|
|
Write-Host "$BLUE⏳ [进度]$NC 4/7 - 读取原始配置到内存..."
|
|
|
|
# 原子性操作:读取原始内容到内存
|
|
$originalContent = Get-Content $configPath -Raw -Encoding UTF8 -ErrorAction Stop
|
|
$config = $originalContent | ConvertFrom-Json -ErrorAction Stop
|
|
|
|
Write-Host "$BLUE⏳ [进度]$NC 5/7 - 在内存中更新配置..."
|
|
|
|
# 更新配置值(安全方式,确保属性存在)
|
|
# 🔧 修复: 添加 storage.serviceMachineId 和 telemetry.firstSessionDate
|
|
$propertiesToUpdate = @{
|
|
'telemetry.machineId' = $MACHINE_ID
|
|
'telemetry.macMachineId' = $MAC_MACHINE_ID
|
|
'telemetry.devDeviceId' = $UUID
|
|
'telemetry.sqmId' = $SQM_ID
|
|
'storage.serviceMachineId' = $SERVICE_MACHINE_ID
|
|
'telemetry.firstSessionDate' = $FIRST_SESSION_DATE
|
|
}
|
|
|
|
foreach ($property in $propertiesToUpdate.GetEnumerator()) {
|
|
$key = $property.Key
|
|
$value = $property.Value
|
|
|
|
# 使用 Add-Member 或直接赋值的安全方式
|
|
if ($config.PSObject.Properties[$key]) {
|
|
# 属性存在,直接更新
|
|
$config.$key = $value
|
|
Write-Host "$BLUE ✓ 更新属性: ${key}$NC"
|
|
} else {
|
|
# 属性不存在,添加新属性
|
|
$config | Add-Member -MemberType NoteProperty -Name $key -Value $value -Force
|
|
Write-Host "$BLUE + 添加属性: ${key}$NC"
|
|
}
|
|
}
|
|
|
|
Write-Host "$BLUE⏳ [进度]$NC 6/7 - 原子性写入新配置文件..."
|
|
|
|
# 原子性操作:删除原文件,写入新文件
|
|
$tempPath = "$configPath.tmp"
|
|
$updatedJson = $config | ConvertTo-Json -Depth 10
|
|
|
|
# 写入临时文件
|
|
[System.IO.File]::WriteAllText($tempPath, $updatedJson, [System.Text.Encoding]::UTF8)
|
|
|
|
# 验证临时文件
|
|
$tempContent = Get-Content $tempPath -Raw -Encoding UTF8
|
|
$tempConfig = $tempContent | ConvertFrom-Json
|
|
|
|
# 验证所有属性是否正确写入
|
|
$tempVerificationPassed = $true
|
|
foreach ($property in $propertiesToUpdate.GetEnumerator()) {
|
|
$key = $property.Key
|
|
$expectedValue = $property.Value
|
|
$actualValue = $tempConfig.$key
|
|
|
|
if ($actualValue -ne $expectedValue) {
|
|
$tempVerificationPassed = $false
|
|
Write-Host "$RED ✗ 临时文件验证失败: ${key}$NC"
|
|
break
|
|
}
|
|
}
|
|
|
|
if (-not $tempVerificationPassed) {
|
|
Remove-Item $tempPath -Force -ErrorAction SilentlyContinue
|
|
throw "临时文件验证失败"
|
|
}
|
|
|
|
# 原子性替换:删除原文件,重命名临时文件
|
|
Remove-Item $configPath -Force
|
|
Move-Item $tempPath $configPath
|
|
|
|
# 设置文件为只读(可选)
|
|
$file = Get-Item $configPath
|
|
$file.IsReadOnly = $false # 保持可写,便于后续修改
|
|
|
|
# 最终验证修改结果
|
|
Write-Host "$BLUE⏳ [进度]$NC 7/7 - 验证新配置文件..."
|
|
|
|
$verifyContent = Get-Content $configPath -Raw -Encoding UTF8
|
|
$verifyConfig = $verifyContent | ConvertFrom-Json
|
|
|
|
$verificationPassed = $true
|
|
$verificationResults = @()
|
|
|
|
# 安全验证每个属性
|
|
foreach ($property in $propertiesToUpdate.GetEnumerator()) {
|
|
$key = $property.Key
|
|
$expectedValue = $property.Value
|
|
$actualValue = $verifyConfig.$key
|
|
|
|
if ($actualValue -eq $expectedValue) {
|
|
$verificationResults += "✓ ${key}: 验证通过"
|
|
} else {
|
|
$verificationResults += "✗ ${key}: 验证失败 (期望: ${expectedValue}, 实际: ${actualValue})"
|
|
$verificationPassed = $false
|
|
}
|
|
}
|
|
|
|
# 显示验证结果
|
|
Write-Host "$BLUE📋 [验证详情]$NC"
|
|
foreach ($result in $verificationResults) {
|
|
Write-Host " $result"
|
|
}
|
|
|
|
if ($verificationPassed) {
|
|
Write-Host "$GREEN✅ [成功]$NC 第 $retryCount 次尝试修改成功!"
|
|
Write-Host ""
|
|
Write-Host "$GREEN🎉 [完成]$NC 机器码配置修改完成!"
|
|
Write-Host "$BLUE📋 [详情]$NC 已更新以下标识符:"
|
|
Write-Host " 🔹 machineId: $MACHINE_ID"
|
|
Write-Host " 🔹 macMachineId: $MAC_MACHINE_ID"
|
|
Write-Host " 🔹 devDeviceId: $UUID"
|
|
Write-Host " 🔹 sqmId: $SQM_ID"
|
|
Write-Host " 🔹 serviceMachineId: $SERVICE_MACHINE_ID"
|
|
Write-Host " 🔹 firstSessionDate: $FIRST_SESSION_DATE"
|
|
Write-Host ""
|
|
Write-Host "$GREEN💾 [备份]$NC 原配置已备份至: $backupName"
|
|
|
|
# 🔧 新增: 修改 machineid 文件
|
|
Write-Host "$BLUE🔧 [machineid]$NC 正在修改 machineid 文件..."
|
|
$machineIdFilePath = if ($global:CursorAppDataDir) { Join-Path $global:CursorAppDataDir "machineid" } else { $null }
|
|
if (-not $machineIdFilePath) {
|
|
Write-Host "$YELLOW⚠️ [machineid]$NC 无法解析 machineid 文件路径,跳过修改"
|
|
} else {
|
|
try {
|
|
if (Test-Path $machineIdFilePath) {
|
|
# 备份原始 machineid 文件
|
|
$machineIdBackup = "$backupDir\machineid.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
|
|
Copy-Item $machineIdFilePath $machineIdBackup -Force
|
|
Write-Host "$GREEN💾 [备份]$NC machineid 文件已备份: $machineIdBackup"
|
|
}
|
|
# 写入新的 serviceMachineId 到 machineid 文件
|
|
[System.IO.File]::WriteAllText($machineIdFilePath, $SERVICE_MACHINE_ID, [System.Text.Encoding]::UTF8)
|
|
Write-Host "$GREEN✅ [machineid]$NC machineid 文件修改成功: $SERVICE_MACHINE_ID"
|
|
|
|
# 设置 machineid 文件为只读
|
|
$machineIdFile = Get-Item $machineIdFilePath
|
|
$machineIdFile.IsReadOnly = $true
|
|
Write-Host "$GREEN🔒 [保护]$NC machineid 文件已设置为只读"
|
|
} catch {
|
|
Write-Host "$YELLOW⚠️ [machineid]$NC machineid 文件修改失败: $($_.Exception.Message)"
|
|
Write-Host "$BLUE💡 [提示]$NC 可手动修改文件: $machineIdFilePath"
|
|
}
|
|
}
|
|
|
|
# 🔧 新增: 修改 .updaterId 文件(更新器设备标识符)
|
|
Write-Host "$BLUE🔧 [updaterId]$NC 正在修改 .updaterId 文件..."
|
|
$updaterIdFilePath = if ($global:CursorAppDataDir) { Join-Path $global:CursorAppDataDir ".updaterId" } else { $null }
|
|
if (-not $updaterIdFilePath) {
|
|
Write-Host "$YELLOW⚠️ [updaterId]$NC 无法解析 .updaterId 文件路径,跳过修改"
|
|
} else {
|
|
try {
|
|
if (Test-Path $updaterIdFilePath) {
|
|
# 备份原始 .updaterId 文件
|
|
$updaterIdBackup = "$backupDir\.updaterId.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
|
|
Copy-Item $updaterIdFilePath $updaterIdBackup -Force
|
|
Write-Host "$GREEN💾 [备份]$NC .updaterId 文件已备份: $updaterIdBackup"
|
|
}
|
|
# 生成新的 updaterId(UUID格式)
|
|
$newUpdaterId = [System.Guid]::NewGuid().ToString()
|
|
[System.IO.File]::WriteAllText($updaterIdFilePath, $newUpdaterId, [System.Text.Encoding]::UTF8)
|
|
Write-Host "$GREEN✅ [updaterId]$NC .updaterId 文件修改成功: $newUpdaterId"
|
|
|
|
# 设置 .updaterId 文件为只读
|
|
$updaterIdFile = Get-Item $updaterIdFilePath
|
|
$updaterIdFile.IsReadOnly = $true
|
|
Write-Host "$GREEN🔒 [保护]$NC .updaterId 文件已设置为只读"
|
|
} catch {
|
|
Write-Host "$YELLOW⚠️ [updaterId]$NC .updaterId 文件修改失败: $($_.Exception.Message)"
|
|
Write-Host "$BLUE💡 [提示]$NC 可手动修改文件: $updaterIdFilePath"
|
|
}
|
|
}
|
|
|
|
# 🔒 添加配置文件保护机制
|
|
Write-Host "$BLUE🔒 [保护]$NC 正在设置配置文件保护..."
|
|
try {
|
|
$configFile = Get-Item $configPath
|
|
$configFile.IsReadOnly = $true
|
|
Write-Host "$GREEN✅ [保护]$NC 配置文件已设置为只读,防止Cursor覆盖修改"
|
|
Write-Host "$BLUE💡 [提示]$NC 文件路径: $configPath"
|
|
} catch {
|
|
Write-Host "$YELLOW⚠️ [保护]$NC 设置只读属性失败: $($_.Exception.Message)"
|
|
Write-Host "$BLUE💡 [建议]$NC 可手动右键文件 → 属性 → 勾选'只读'"
|
|
}
|
|
Write-Host "$BLUE 🔒 [安全]$NC 建议重启Cursor以确保配置生效"
|
|
return $true
|
|
} else {
|
|
Write-Host "$RED❌ [失败]$NC 第 $retryCount 次尝试验证失败"
|
|
if ($retryCount -lt $maxRetries) {
|
|
Write-Host "$BLUE🔄 [恢复]$NC 恢复备份,准备重试..."
|
|
Copy-Item $backupPath $configPath -Force
|
|
Start-Sleep -Seconds 2
|
|
continue # 继续下一次重试
|
|
} else {
|
|
Write-Host "$RED❌ [最终失败]$NC 所有重试都失败,恢复原始配置"
|
|
Copy-Item $backupPath $configPath -Force
|
|
return $false
|
|
}
|
|
}
|
|
|
|
} catch {
|
|
Write-Host "$RED❌ [异常]$NC 第 $retryCount 次尝试出现异常: $($_.Exception.Message)"
|
|
Write-Host "$BLUE💡 [调试信息]$NC 错误类型: $($_.Exception.GetType().FullName)"
|
|
|
|
# 清理临时文件
|
|
if (Test-Path "$configPath.tmp") {
|
|
Remove-Item "$configPath.tmp" -Force -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
if ($retryCount -lt $maxRetries) {
|
|
Write-Host "$BLUE🔄 [恢复]$NC 恢复备份,准备重试..."
|
|
if (Test-Path $backupPath) {
|
|
Copy-Item $backupPath $configPath -Force
|
|
}
|
|
Start-Sleep -Seconds 3
|
|
continue # 继续下一次重试
|
|
} else {
|
|
Write-Host "$RED❌ [最终失败]$NC 所有重试都失败"
|
|
# 尝试恢复备份
|
|
if (Test-Path $backupPath) {
|
|
Write-Host "$BLUE🔄 [恢复]$NC 正在恢复备份配置..."
|
|
try {
|
|
Copy-Item $backupPath $configPath -Force
|
|
Write-Host "$GREEN✅ [恢复]$NC 已恢复原始配置"
|
|
} catch {
|
|
Write-Host "$RED❌ [错误]$NC 恢复备份失败: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
return $false
|
|
}
|
|
}
|
|
}
|
|
|
|
# 如果到达这里,说明所有重试都失败了
|
|
Write-Host "$RED❌ [最终失败]$NC 经过 $maxRetries 次尝试仍无法完成修改"
|
|
return $false
|
|
|
|
}
|
|
|
|
# 启动Cursor生成配置文件
|
|
function Start-CursorToGenerateConfig {
|
|
Write-Host "$BLUE🚀 [启动]$NC 正在尝试启动Cursor生成配置文件..."
|
|
|
|
# 查找Cursor可执行文件(支持自动检测 + 手动兜底)
|
|
$installPath = Resolve-CursorInstallPath -AllowPrompt
|
|
$cursorPath = if ($installPath) { Join-Path $installPath "Cursor.exe" } else { $null }
|
|
|
|
if (-not $cursorPath) {
|
|
Write-Host "$RED❌ [错误]$NC 未找到Cursor安装,请确认Cursor已正确安装"
|
|
return $false
|
|
}
|
|
|
|
try {
|
|
Write-Host "$BLUE📍 [路径]$NC 使用Cursor路径: $cursorPath"
|
|
|
|
# 启动Cursor
|
|
$process = Start-Process -FilePath $cursorPath -PassThru -WindowStyle Normal
|
|
Write-Host "$GREEN🚀 [启动]$NC Cursor已启动,PID: $($process.Id)"
|
|
|
|
Write-Host "$YELLOW⏳ [等待]$NC 请等待Cursor完全加载(约30秒)..."
|
|
Write-Host "$BLUE💡 [提示]$NC 您可以在Cursor完全加载后手动关闭它"
|
|
|
|
# 等待配置文件生成
|
|
$configPath = $STORAGE_FILE
|
|
if (-not $configPath) {
|
|
Write-Host "$RED❌ [错误]$NC 无法解析配置文件路径"
|
|
return $false
|
|
}
|
|
$maxWait = 60
|
|
$waited = 0
|
|
|
|
while (-not (Test-Path $configPath) -and $waited -lt $maxWait) {
|
|
Start-Sleep -Seconds 2
|
|
$waited += 2
|
|
if ($waited % 10 -eq 0) {
|
|
Write-Host "$YELLOW⏳ [等待]$NC 等待配置文件生成... ($waited/$maxWait 秒)"
|
|
}
|
|
}
|
|
|
|
if (Test-Path $configPath) {
|
|
Write-Host "$GREEN✅ [成功]$NC 配置文件已生成!"
|
|
Write-Host "$BLUE💡 [提示]$NC 现在可以关闭Cursor并重新运行脚本"
|
|
return $true
|
|
} else {
|
|
Write-Host "$YELLOW⚠️ [超时]$NC 配置文件未在预期时间内生成"
|
|
Write-Host "$BLUE💡 [建议]$NC 请手动操作Cursor(如创建新文件)以触发配置生成"
|
|
return $false
|
|
}
|
|
|
|
} catch {
|
|
Write-Host "$RED❌ [错误]$NC 启动Cursor失败: $($_.Exception.Message)"
|
|
return $false
|
|
}
|
|
}
|
|
|
|
# 检查管理员权限
|
|
function Test-Administrator {
|
|
$user = [Security.Principal.WindowsIdentity]::GetCurrent()
|
|
$principal = New-Object Security.Principal.WindowsPrincipal($user)
|
|
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
|
}
|
|
|
|
if (-not (Test-Administrator)) {
|
|
Write-Host "$RED[错误]$NC 请以管理员身份运行此脚本"
|
|
Write-Host "请右键点击脚本,选择'以管理员身份运行'"
|
|
Read-Host "按回车键退出"
|
|
exit 1
|
|
}
|
|
|
|
# 显示 Logo
|
|
Clear-Host
|
|
Write-Host @"
|
|
|
|
██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗
|
|
██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗
|
|
██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝
|
|
██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗
|
|
╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║
|
|
╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝
|
|
|
|
"@
|
|
Write-Host "$BLUE================================$NC"
|
|
Write-Host "$GREEN🚀 Cursor 防掉试用Pro删除工具 $NC"
|
|
Write-Host "$YELLOW📱 关注公众号【煎饼果子卷AI】 $NC"
|
|
Write-Host "$YELLOW🤝 一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) $NC"
|
|
Write-Host "$YELLOW💡 [重要提示] 本工具免费,如果对您有帮助,请关注公众号【煎饼果子卷AI】 $NC"
|
|
Write-Host ""
|
|
Write-Host "$YELLOW⚡ [小小广告] Cursor官网正规成品号:Pro¥65 | Pro+¥265 | Ultra¥888 独享账号| ¥688 Team绝版次数号1000次+20刀额度 | 全部7天质保 | ,WeChat:JavaRookie666 $NC"
|
|
Write-Host "$BLUE================================$NC"
|
|
|
|
# 🎯 用户选择菜单
|
|
Write-Host ""
|
|
Write-Host "$GREEN🎯 [选择模式]$NC 请选择您要执行的操作:"
|
|
Write-Host ""
|
|
Write-Host "$BLUE 1️⃣ 仅修改机器码$NC"
|
|
Write-Host "$YELLOW • 执行机器码修改功能$NC"
|
|
Write-Host "$YELLOW • 执行注入破解JS代码到核心文件$NC"
|
|
Write-Host "$YELLOW • 跳过文件夹删除/环境重置步骤$NC"
|
|
Write-Host "$YELLOW • 保留现有Cursor配置和数据$NC"
|
|
Write-Host ""
|
|
Write-Host "$BLUE 2️⃣ 重置环境+修改机器码$NC"
|
|
Write-Host "$RED • 执行完全环境重置(删除Cursor文件夹)$NC"
|
|
Write-Host "$RED • ⚠️ 配置将丢失,请注意备份$NC"
|
|
Write-Host "$YELLOW • 按照机器代码修改$NC"
|
|
Write-Host "$YELLOW • 执行注入破解JS代码到核心文件$NC"
|
|
Write-Host "$YELLOW • 这相当于当前的完整脚本行为$NC"
|
|
Write-Host ""
|
|
|
|
# 获取用户选择
|
|
do {
|
|
$userChoice = Read-Host "请输入选择 (1 或 2)"
|
|
if ($userChoice -eq "1") {
|
|
Write-Host "$GREEN✅ [选择]$NC 您选择了:仅修改机器码"
|
|
$executeMode = "MODIFY_ONLY"
|
|
break
|
|
} elseif ($userChoice -eq "2") {
|
|
Write-Host "$GREEN✅ [选择]$NC 您选择了:重置环境+修改机器码"
|
|
Write-Host "$RED⚠️ [重要警告]$NC 此操作将删除所有Cursor配置文件!"
|
|
$confirmReset = Read-Host "确认执行完全重置?(输入 yes 确认,其他任意键取消)"
|
|
if ($confirmReset -eq "yes") {
|
|
$executeMode = "RESET_AND_MODIFY"
|
|
break
|
|
} else {
|
|
Write-Host "$YELLOW👋 [取消]$NC 用户取消重置操作"
|
|
continue
|
|
}
|
|
} else {
|
|
Write-Host "$RED❌ [错误]$NC 无效选择,请输入 1 或 2"
|
|
}
|
|
} while ($true)
|
|
|
|
Write-Host ""
|
|
|
|
# 📋 根据选择显示执行流程说明
|
|
if ($executeMode -eq "MODIFY_ONLY") {
|
|
Write-Host "$GREEN📋 [执行流程]$NC 仅修改机器码模式将按以下步骤执行:"
|
|
Write-Host "$BLUE 1️⃣ 检测Cursor配置文件$NC"
|
|
Write-Host "$BLUE 2️⃣ 备份现有配置文件$NC"
|
|
Write-Host "$BLUE 3️⃣ 修改机器码配置$NC"
|
|
Write-Host "$BLUE 4️⃣ 显示操作完成信息$NC"
|
|
Write-Host ""
|
|
Write-Host "$YELLOW⚠️ [注意事项]$NC"
|
|
Write-Host "$YELLOW • 不会删除任何文件夹或重置环境$NC"
|
|
Write-Host "$YELLOW • 保留所有现有配置和数据$NC"
|
|
Write-Host "$YELLOW • 原配置文件会自动备份$NC"
|
|
} else {
|
|
Write-Host "$GREEN📋 [执行流程]$NC 重置环境+修改机器码模式将按以下步骤执行:"
|
|
Write-Host "$BLUE 1️⃣ 检测并关闭Cursor进程$NC"
|
|
Write-Host "$BLUE 2️⃣ 保存Cursor程序路径信息$NC"
|
|
Write-Host "$BLUE 3️⃣ 删除指定的Cursor试用相关文件夹$NC"
|
|
Write-Host "$BLUE 📁 C:\Users\Administrator\.cursor$NC"
|
|
Write-Host "$BLUE 📁 C:\Users\Administrator\AppData\Roaming\Cursor$NC"
|
|
Write-Host "$BLUE 📁 C:\Users\%USERNAME%\.cursor$NC"
|
|
Write-Host "$BLUE 📁 C:\Users\%USERNAME%\AppData\Roaming\Cursor$NC"
|
|
Write-Host "$BLUE 3.5️⃣ 预创建必要目录结构,避免权限问题$NC"
|
|
Write-Host "$BLUE 4️⃣ 重新启动Cursor让其生成新的配置文件$NC"
|
|
Write-Host "$BLUE 5️⃣ 等待配置文件生成完成(最多45秒)$NC"
|
|
Write-Host "$BLUE 6️⃣ 关闭Cursor进程$NC"
|
|
Write-Host "$BLUE 7️⃣ 修改新生成的机器码配置文件$NC"
|
|
Write-Host "$BLUE 8️⃣ 显示操作完成统计信息$NC"
|
|
Write-Host ""
|
|
Write-Host "$YELLOW⚠️ [注意事项]$NC"
|
|
Write-Host "$YELLOW • 脚本执行过程中请勿手动操作Cursor$NC"
|
|
Write-Host "$YELLOW • 建议在执行前关闭所有Cursor窗口$NC"
|
|
Write-Host "$YELLOW • 执行完成后需要重新启动Cursor$NC"
|
|
Write-Host "$YELLOW • 原配置文件会自动备份到backups文件夹$NC"
|
|
}
|
|
Write-Host ""
|
|
|
|
# 🤔 用户确认
|
|
Write-Host "$GREEN🤔 [确认]$NC 请确认您已了解上述执行流程"
|
|
$confirmation = Read-Host "是否继续执行?(输入 y 或 yes 继续,其他任意键退出)"
|
|
if ($confirmation -notmatch "^(y|yes)$") {
|
|
Write-Host "$YELLOW👋 [退出]$NC 用户取消执行,脚本退出"
|
|
Read-Host "按回车键退出"
|
|
exit 0
|
|
}
|
|
Write-Host "$GREEN✅ [确认]$NC 用户确认继续执行"
|
|
Write-Host ""
|
|
|
|
# 获取并显示 Cursor 版本
|
|
function Get-CursorVersion {
|
|
try {
|
|
# 主要检测路径(基于安装路径解析)
|
|
$installPath = Resolve-CursorInstallPath
|
|
$packagePath = if ($installPath) { Join-Path $installPath "resources\app\package.json" } else { $null }
|
|
if ($packagePath -and (Test-Path $packagePath)) {
|
|
$packageJson = Get-Content $packagePath -Raw | ConvertFrom-Json
|
|
if ($packageJson.version) {
|
|
Write-Host "$GREEN[信息]$NC 当前安装的 Cursor 版本: v$($packageJson.version)"
|
|
return $packageJson.version
|
|
}
|
|
}
|
|
|
|
# 备用路径检测(兼容旧目录结构)
|
|
$altPath = if ($global:CursorLocalAppDataRoot) { Join-Path $global:CursorLocalAppDataRoot "cursor\resources\app\package.json" } else { $null }
|
|
if ($altPath -and (Test-Path $altPath)) {
|
|
$packageJson = Get-Content $altPath -Raw | ConvertFrom-Json
|
|
if ($packageJson.version) {
|
|
Write-Host "$GREEN[信息]$NC 当前安装的 Cursor 版本: v$($packageJson.version)"
|
|
return $packageJson.version
|
|
}
|
|
}
|
|
|
|
Write-Host "$YELLOW[警告]$NC 无法检测到 Cursor 版本"
|
|
Write-Host "$YELLOW[提示]$NC 请确保 Cursor 已正确安装"
|
|
return $null
|
|
}
|
|
catch {
|
|
Write-Host "$RED[错误]$NC 获取 Cursor 版本失败: $_"
|
|
return $null
|
|
}
|
|
}
|
|
|
|
# 获取并显示版本信息
|
|
$cursorVersion = Get-CursorVersion
|
|
Write-Host ""
|
|
|
|
Write-Host "$YELLOW💡 [重要提示]$NC 最新的 1.0.x 版本已支持"
|
|
|
|
Write-Host ""
|
|
|
|
# 🔍 检查并关闭 Cursor 进程
|
|
Write-Host "$GREEN🔍 [检查]$NC 正在检查 Cursor 进程..."
|
|
|
|
function Get-ProcessDetails {
|
|
param($processName)
|
|
Write-Host "$BLUE🔍 [调试]$NC 正在获取 $processName 进程详细信息:"
|
|
Get-WmiObject Win32_Process -Filter "name='$processName'" |
|
|
Select-Object ProcessId, ExecutablePath, CommandLine |
|
|
Format-List
|
|
}
|
|
|
|
# 定义最大重试次数和等待时间
|
|
$MAX_RETRIES = 5
|
|
$WAIT_TIME = 1
|
|
|
|
# 🔄 处理进程关闭并保存进程信息
|
|
function Close-CursorProcessAndSaveInfo {
|
|
param($processName)
|
|
|
|
$global:CursorProcessInfo = $null
|
|
|
|
$processes = Get-Process -Name $processName -ErrorAction SilentlyContinue
|
|
if ($processes) {
|
|
Write-Host "$YELLOW⚠️ [警告]$NC 发现 $processName 正在运行"
|
|
|
|
# 💾 保存进程信息用于后续重启 - 修复:确保获取单个进程路径
|
|
$firstProcess = if ($processes -is [array]) { $processes[0] } else { $processes }
|
|
$processPath = $firstProcess.Path
|
|
|
|
# 确保路径是字符串而不是数组
|
|
if ($processPath -is [array]) {
|
|
$processPath = $processPath[0]
|
|
}
|
|
|
|
$global:CursorProcessInfo = @{
|
|
ProcessName = $firstProcess.ProcessName
|
|
Path = $processPath
|
|
StartTime = $firstProcess.StartTime
|
|
}
|
|
Write-Host "$GREEN💾 [保存]$NC 已保存进程信息: $($global:CursorProcessInfo.Path)"
|
|
|
|
Get-ProcessDetails $processName
|
|
|
|
Write-Host "$YELLOW🔄 [操作]$NC 尝试关闭 $processName..."
|
|
Stop-Process -Name $processName -Force
|
|
|
|
$retryCount = 0
|
|
while ($retryCount -lt $MAX_RETRIES) {
|
|
$process = Get-Process -Name $processName -ErrorAction SilentlyContinue
|
|
if (-not $process) { break }
|
|
|
|
$retryCount++
|
|
if ($retryCount -ge $MAX_RETRIES) {
|
|
Write-Host "$RED❌ [错误]$NC 在 $MAX_RETRIES 次尝试后仍无法关闭 $processName"
|
|
Get-ProcessDetails $processName
|
|
Write-Host "$RED💥 [错误]$NC 请手动关闭进程后重试"
|
|
Read-Host "按回车键退出"
|
|
exit 1
|
|
}
|
|
Write-Host "$YELLOW⏳ [等待]$NC 等待进程关闭,尝试 $retryCount/$MAX_RETRIES..."
|
|
Start-Sleep -Seconds $WAIT_TIME
|
|
}
|
|
Write-Host "$GREEN✅ [成功]$NC $processName 已成功关闭"
|
|
} else {
|
|
Write-Host "$BLUE💡 [提示]$NC 未发现 $processName 进程运行"
|
|
# 尝试找到Cursor的安装路径
|
|
$installPath = Resolve-CursorInstallPath
|
|
$candidatePath = if ($installPath) { Join-Path $installPath "Cursor.exe" } else { $null }
|
|
if ($candidatePath -and (Test-Path $candidatePath)) {
|
|
$global:CursorProcessInfo = @{
|
|
ProcessName = "Cursor"
|
|
Path = $candidatePath
|
|
StartTime = $null
|
|
}
|
|
Write-Host "$GREEN💾 [发现]$NC 找到Cursor安装路径: $candidatePath"
|
|
}
|
|
|
|
if (-not $global:CursorProcessInfo) {
|
|
Write-Host "$YELLOW⚠️ [警告]$NC 未找到Cursor安装路径,将使用默认路径"
|
|
$defaultInstallPath = if ($global:CursorLocalAppDataRoot) { Join-Path $global:CursorLocalAppDataRoot "Programs\cursor\Cursor.exe" } else { "$env:LOCALAPPDATA\Programs\cursor\Cursor.exe" }
|
|
$global:CursorProcessInfo = @{
|
|
ProcessName = "Cursor"
|
|
Path = $defaultInstallPath
|
|
StartTime = $null
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# �️ 确保备份目录存在
|
|
if (-not $BACKUP_DIR) {
|
|
Write-Host "$YELLOW⚠️ [警告]$NC 无法解析备份目录路径,跳过创建"
|
|
} elseif (-not (Test-Path $BACKUP_DIR)) {
|
|
try {
|
|
New-Item -ItemType Directory -Path $BACKUP_DIR -Force | Out-Null
|
|
Write-Host "$GREEN✅ [备份目录]$NC 备份目录创建成功: $BACKUP_DIR"
|
|
} catch {
|
|
Write-Host "$YELLOW⚠️ [警告]$NC 备份目录创建失败: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# �🚀 根据用户选择执行相应功能
|
|
if ($executeMode -eq "MODIFY_ONLY") {
|
|
Write-Host "$GREEN🚀 [开始]$NC 开始执行仅修改机器码功能..."
|
|
|
|
# 先进行环境检查
|
|
$envCheck = Test-CursorEnvironment -Mode "MODIFY_ONLY"
|
|
if (-not $envCheck.Success) {
|
|
Write-Host ""
|
|
Write-Host "$RED❌ [环境检查失败]$NC 无法继续执行,发现以下问题:"
|
|
foreach ($issue in $envCheck.Issues) {
|
|
Write-Host "$RED • ${issue}$NC"
|
|
}
|
|
Write-Host ""
|
|
Write-Host "$YELLOW💡 [建议]$NC 请选择以下操作:"
|
|
Write-Host "$BLUE 1️⃣ 选择'重置环境+修改机器码'选项(推荐)$NC"
|
|
Write-Host "$BLUE 2️⃣ 手动启动Cursor一次,然后重新运行脚本$NC"
|
|
Write-Host "$BLUE 3️⃣ 检查Cursor是否正确安装$NC"
|
|
Write-Host ""
|
|
Read-Host "按回车键退出"
|
|
exit 1
|
|
}
|
|
|
|
# 执行机器码修改
|
|
$configSuccess = Modify-MachineCodeConfig -Mode "MODIFY_ONLY"
|
|
|
|
if ($configSuccess) {
|
|
Write-Host ""
|
|
Write-Host "$GREEN🎉 [配置文件]$NC 机器码配置文件修改完成!"
|
|
|
|
# 添加注册表修改
|
|
Write-Host "$BLUE🔧 [注册表]$NC 正在修改系统注册表..."
|
|
$registrySuccess = Update-MachineGuid
|
|
|
|
# 🔧 新增:JavaScript注入功能(设备识别绕过增强)
|
|
Write-Host ""
|
|
Write-Host "$BLUE🔧 [设备识别绕过]$NC 正在执行JavaScript注入功能..."
|
|
Write-Host "$BLUE💡 [说明]$NC 此功能将直接修改Cursor内核JS文件,实现更深层的设备识别绕过"
|
|
$jsSuccess = Modify-CursorJSFiles
|
|
|
|
if ($registrySuccess) {
|
|
Write-Host "$GREEN✅ [注册表]$NC 系统注册表修改成功"
|
|
|
|
if ($jsSuccess) {
|
|
Write-Host "$GREEN✅ [JavaScript注入]$NC JavaScript注入功能执行成功"
|
|
Write-Host ""
|
|
Write-Host "$GREEN🎉 [完成]$NC 所有机器码修改完成(增强版)!"
|
|
Write-Host "$BLUE📋 [详情]$NC 已完成以下修改:"
|
|
Write-Host "$GREEN ✓ Cursor 配置文件 (storage.json)$NC"
|
|
Write-Host "$GREEN ✓ 系统注册表 (MachineGuid)$NC"
|
|
Write-Host "$GREEN ✓ JavaScript内核注入(设备识别绕过)$NC"
|
|
} else {
|
|
Write-Host "$YELLOW⚠️ [JavaScript注入]$NC JavaScript注入功能执行失败,但其他功能成功"
|
|
Write-Host ""
|
|
Write-Host "$GREEN🎉 [完成]$NC 所有机器码修改完成!"
|
|
Write-Host "$BLUE📋 [详情]$NC 已完成以下修改:"
|
|
Write-Host "$GREEN ✓ Cursor 配置文件 (storage.json)$NC"
|
|
Write-Host "$GREEN ✓ 系统注册表 (MachineGuid)$NC"
|
|
Write-Host "$YELLOW ⚠ JavaScript内核注入(部分失败)$NC"
|
|
}
|
|
|
|
# 🔒 添加配置文件保护机制
|
|
Write-Host "$BLUE🔒 [保护]$NC 正在设置配置文件保护..."
|
|
try {
|
|
$configPath = $STORAGE_FILE
|
|
if (-not $configPath) {
|
|
throw "无法解析配置文件路径"
|
|
}
|
|
$configFile = Get-Item $configPath
|
|
$configFile.IsReadOnly = $true
|
|
Write-Host "$GREEN✅ [保护]$NC 配置文件已设置为只读,防止Cursor覆盖修改"
|
|
Write-Host "$BLUE💡 [提示]$NC 文件路径: $configPath"
|
|
} catch {
|
|
Write-Host "$YELLOW⚠️ [保护]$NC 设置只读属性失败: $($_.Exception.Message)"
|
|
Write-Host "$BLUE💡 [建议]$NC 可手动右键文件 → 属性 → 勾选'只读'"
|
|
}
|
|
} else {
|
|
Write-Host "$YELLOW⚠️ [注册表]$NC 注册表修改失败,但配置文件修改成功"
|
|
|
|
if ($jsSuccess) {
|
|
Write-Host "$GREEN✅ [JavaScript注入]$NC JavaScript注入功能执行成功"
|
|
Write-Host ""
|
|
Write-Host "$YELLOW🎉 [部分完成]$NC 配置文件和JavaScript注入完成,注册表修改失败"
|
|
Write-Host "$BLUE💡 [建议]$NC 可能需要管理员权限来修改注册表"
|
|
Write-Host "$BLUE📋 [详情]$NC 已完成以下修改:"
|
|
Write-Host "$GREEN ✓ Cursor 配置文件 (storage.json)$NC"
|
|
Write-Host "$YELLOW ⚠ 系统注册表 (MachineGuid) - 失败$NC"
|
|
Write-Host "$GREEN ✓ JavaScript内核注入(设备识别绕过)$NC"
|
|
} else {
|
|
Write-Host "$YELLOW⚠️ [JavaScript注入]$NC JavaScript注入功能执行失败"
|
|
Write-Host ""
|
|
Write-Host "$YELLOW🎉 [部分完成]$NC 配置文件修改完成,注册表和JavaScript注入失败"
|
|
Write-Host "$BLUE💡 [建议]$NC 可能需要管理员权限来修改注册表"
|
|
}
|
|
|
|
# 🔒 即使注册表修改失败,也要保护配置文件
|
|
Write-Host "$BLUE🔒 [保护]$NC 正在设置配置文件保护..."
|
|
try {
|
|
$configPath = $STORAGE_FILE
|
|
if (-not $configPath) {
|
|
throw "无法解析配置文件路径"
|
|
}
|
|
$configFile = Get-Item $configPath
|
|
$configFile.IsReadOnly = $true
|
|
Write-Host "$GREEN✅ [保护]$NC 配置文件已设置为只读,防止Cursor覆盖修改"
|
|
Write-Host "$BLUE💡 [提示]$NC 文件路径: $configPath"
|
|
} catch {
|
|
Write-Host "$YELLOW⚠️ [保护]$NC 设置只读属性失败: $($_.Exception.Message)"
|
|
Write-Host "$BLUE💡 [建议]$NC 可手动右键文件 → 属性 → 勾选'只读'"
|
|
}
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "$BLUE🚫 [禁用更新]$NC 正在禁用 Cursor 自动更新..."
|
|
if (Disable-CursorAutoUpdate) {
|
|
Write-Host "$GREEN✅ [禁用更新]$NC 自动更新已处理"
|
|
} else {
|
|
Write-Host "$YELLOW⚠️ [禁用更新]$NC 未能确认禁用更新,可能需要手动处理"
|
|
}
|
|
|
|
Write-Host "$BLUE💡 [提示]$NC 现在可以启动Cursor使用新的机器码配置"
|
|
} else {
|
|
Write-Host ""
|
|
Write-Host "$RED❌ [失败]$NC 机器码修改失败!"
|
|
Write-Host "$YELLOW💡 [建议]$NC 请尝试'重置环境+修改机器码'选项"
|
|
}
|
|
} else {
|
|
# 完整的重置环境+修改机器码流程
|
|
Write-Host "$GREEN🚀 [开始]$NC 开始执行重置环境+修改机器码功能..."
|
|
|
|
# 🚀 关闭所有 Cursor 进程并保存信息
|
|
Close-CursorProcessAndSaveInfo "Cursor"
|
|
if (-not $global:CursorProcessInfo) {
|
|
Close-CursorProcessAndSaveInfo "cursor"
|
|
}
|
|
|
|
# 🚨 重要警告提示
|
|
Write-Host ""
|
|
Write-Host "$RED🚨 [重要警告]$NC ============================================"
|
|
Write-Host "$YELLOW⚠️ [风控提醒]$NC Cursor 风控机制非常严格!"
|
|
Write-Host "$YELLOW⚠️ [必须删除]$NC 必须完全删除指定文件夹,不能有任何残留设置"
|
|
Write-Host "$YELLOW⚠️ [防掉试用]$NC 只有彻底清理才能有效防止掉试用Pro状态"
|
|
Write-Host "$RED🚨 [重要警告]$NC ============================================"
|
|
Write-Host ""
|
|
|
|
# 🎯 执行 Cursor 防掉试用Pro删除文件夹功能
|
|
Write-Host "$GREEN🚀 [开始]$NC 开始执行核心功能..."
|
|
Remove-CursorTrialFolders
|
|
|
|
|
|
|
|
# 🔄 重启Cursor让其重新生成配置文件
|
|
Restart-CursorAndWait
|
|
|
|
# 🛠️ 修改机器码配置
|
|
$configSuccess = Modify-MachineCodeConfig
|
|
|
|
# 🧹 执行 Cursor 初始化清理
|
|
Invoke-CursorInitialization
|
|
|
|
if ($configSuccess) {
|
|
Write-Host ""
|
|
Write-Host "$GREEN🎉 [配置文件]$NC 机器码配置文件修改完成!"
|
|
|
|
# 添加注册表修改
|
|
Write-Host "$BLUE🔧 [注册表]$NC 正在修改系统注册表..."
|
|
$registrySuccess = Update-MachineGuid
|
|
|
|
# 🔧 新增:JavaScript注入功能(设备识别绕过增强)
|
|
Write-Host ""
|
|
Write-Host "$BLUE🔧 [设备识别绕过]$NC 正在执行JavaScript注入功能..."
|
|
Write-Host "$BLUE💡 [说明]$NC 此功能将直接修改Cursor内核JS文件,实现更深层的设备识别绕过"
|
|
$jsSuccess = Modify-CursorJSFiles
|
|
|
|
if ($registrySuccess) {
|
|
Write-Host "$GREEN✅ [注册表]$NC 系统注册表修改成功"
|
|
|
|
if ($jsSuccess) {
|
|
Write-Host "$GREEN✅ [JavaScript注入]$NC JavaScript注入功能执行成功"
|
|
Write-Host ""
|
|
Write-Host "$GREEN🎉 [完成]$NC 所有操作完成(增强版)!"
|
|
Write-Host "$BLUE📋 [详情]$NC 已完成以下操作:"
|
|
Write-Host "$GREEN ✓ 删除 Cursor 试用相关文件夹$NC"
|
|
Write-Host "$GREEN ✓ Cursor 初始化清理$NC"
|
|
Write-Host "$GREEN ✓ 重新生成配置文件$NC"
|
|
Write-Host "$GREEN ✓ 修改机器码配置$NC"
|
|
Write-Host "$GREEN ✓ 修改系统注册表$NC"
|
|
Write-Host "$GREEN ✓ JavaScript内核注入(设备识别绕过)$NC"
|
|
} else {
|
|
Write-Host "$YELLOW⚠️ [JavaScript注入]$NC JavaScript注入功能执行失败,但其他功能成功"
|
|
Write-Host ""
|
|
Write-Host "$GREEN🎉 [完成]$NC 所有操作完成!"
|
|
Write-Host "$BLUE📋 [详情]$NC 已完成以下操作:"
|
|
Write-Host "$GREEN ✓ 删除 Cursor 试用相关文件夹$NC"
|
|
Write-Host "$GREEN ✓ Cursor 初始化清理$NC"
|
|
Write-Host "$GREEN ✓ 重新生成配置文件$NC"
|
|
Write-Host "$GREEN ✓ 修改机器码配置$NC"
|
|
Write-Host "$GREEN ✓ 修改系统注册表$NC"
|
|
Write-Host "$YELLOW ⚠ JavaScript内核注入(部分失败)$NC"
|
|
}
|
|
|
|
# 🔒 添加配置文件保护机制
|
|
Write-Host "$BLUE🔒 [保护]$NC 正在设置配置文件保护..."
|
|
try {
|
|
$configPath = $STORAGE_FILE
|
|
if (-not $configPath) {
|
|
throw "无法解析配置文件路径"
|
|
}
|
|
$configFile = Get-Item $configPath
|
|
$configFile.IsReadOnly = $true
|
|
Write-Host "$GREEN✅ [保护]$NC 配置文件已设置为只读,防止Cursor覆盖修改"
|
|
Write-Host "$BLUE💡 [提示]$NC 文件路径: $configPath"
|
|
} catch {
|
|
Write-Host "$YELLOW⚠️ [保护]$NC 设置只读属性失败: $($_.Exception.Message)"
|
|
Write-Host "$BLUE💡 [建议]$NC 可手动右键文件 → 属性 → 勾选'只读'"
|
|
}
|
|
} else {
|
|
Write-Host "$YELLOW⚠️ [注册表]$NC 注册表修改失败,但其他操作成功"
|
|
|
|
if ($jsSuccess) {
|
|
Write-Host "$GREEN✅ [JavaScript注入]$NC JavaScript注入功能执行成功"
|
|
Write-Host ""
|
|
Write-Host "$YELLOW🎉 [部分完成]$NC 大部分操作完成,注册表修改失败"
|
|
Write-Host "$BLUE💡 [建议]$NC 可能需要管理员权限来修改注册表"
|
|
Write-Host "$BLUE📋 [详情]$NC 已完成以下操作:"
|
|
Write-Host "$GREEN ✓ 删除 Cursor 试用相关文件夹$NC"
|
|
Write-Host "$GREEN ✓ Cursor 初始化清理$NC"
|
|
Write-Host "$GREEN ✓ 重新生成配置文件$NC"
|
|
Write-Host "$GREEN ✓ 修改机器码配置$NC"
|
|
Write-Host "$YELLOW ⚠ 修改系统注册表 - 失败$NC"
|
|
Write-Host "$GREEN ✓ JavaScript内核注入(设备识别绕过)$NC"
|
|
} else {
|
|
Write-Host "$YELLOW⚠️ [JavaScript注入]$NC JavaScript注入功能执行失败"
|
|
Write-Host ""
|
|
Write-Host "$YELLOW🎉 [部分完成]$NC 大部分操作完成,注册表和JavaScript注入失败"
|
|
Write-Host "$BLUE💡 [建议]$NC 可能需要管理员权限来修改注册表"
|
|
}
|
|
|
|
# 🔒 即使注册表修改失败,也要保护配置文件
|
|
Write-Host "$BLUE🔒 [保护]$NC 正在设置配置文件保护..."
|
|
try {
|
|
$configPath = $STORAGE_FILE
|
|
if (-not $configPath) {
|
|
throw "无法解析配置文件路径"
|
|
}
|
|
$configFile = Get-Item $configPath
|
|
$configFile.IsReadOnly = $true
|
|
Write-Host "$GREEN✅ [保护]$NC 配置文件已设置为只读,防止Cursor覆盖修改"
|
|
Write-Host "$BLUE💡 [提示]$NC 文件路径: $configPath"
|
|
} catch {
|
|
Write-Host "$YELLOW⚠️ [保护]$NC 设置只读属性失败: $($_.Exception.Message)"
|
|
Write-Host "$BLUE💡 [建议]$NC 可手动右键文件 → 属性 → 勾选'只读'"
|
|
}
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "$BLUE🚫 [禁用更新]$NC 正在禁用 Cursor 自动更新..."
|
|
if (Disable-CursorAutoUpdate) {
|
|
Write-Host "$GREEN✅ [禁用更新]$NC 自动更新已处理"
|
|
} else {
|
|
Write-Host "$YELLOW⚠️ [禁用更新]$NC 未能确认禁用更新,可能需要手动处理"
|
|
}
|
|
} else {
|
|
Write-Host ""
|
|
Write-Host "$RED❌ [失败]$NC 机器码配置修改失败!"
|
|
Write-Host "$YELLOW💡 [建议]$NC 请检查错误信息并重试"
|
|
}
|
|
}
|
|
|
|
|
|
# 📱 显示公众号信息
|
|
Write-Host ""
|
|
Write-Host "$GREEN================================$NC"
|
|
Write-Host "$YELLOW📱 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) $NC"
|
|
Write-Host "$YELLOW⚡ [小小广告] Cursor官网正规成品号:Pro¥65 | Pro+¥265 | Ultra¥888 独享账号| ¥688 Team绝版次数号1000次+20刀额度 | 全部7天质保 | ,WeChat:JavaRookie666 $NC"
|
|
Write-Host "$GREEN================================$NC"
|
|
Write-Host ""
|
|
|
|
# 🎉 脚本执行完成
|
|
Write-Host "$GREEN🎉 [脚本完成]$NC 感谢使用 Cursor 机器码修改工具!"
|
|
Write-Host "$BLUE💡 [提示]$NC 如有问题请参考公众号或重新运行脚本"
|
|
Write-Host ""
|
|
Read-Host "按回车键退出"
|
|
exit 0
|