3 Commits
main ... v3.2.6

Author SHA1 Message Date
huangzhenpc
56b619c4dc chore: update version after GUI migration 2025-02-12 13:34:13 +08:00
huangzhenpc
c58903846d refactor: migrate GUI from tkinter to PyQt5
主要改动:

1. GUI框架迁移 - 从 tkinter 完全迁移到 PyQt5 - 重写了所有界面组件和布局 - 优化了界面交互逻辑

2. 界面改进 - 使用 QMainWindow 作为主窗口 - 采用 QVBoxLayout 和 QHBoxLayout 进行布局 - 使用 QFrame 组织界面结构 - 添加了状态显示区域的滚动功能

3. 功能优化 - 改进了错误处理和消息显示 - 使用 QMessageBox 替代 tkinter messagebox - 添加了剪贴板操作功能 - 优化了状态更新逻辑

4. 性能提升 - 移除了不必要的窗口刷新 - 使用 QTimer 优化状态检查 - 简化了窗口管理逻辑

5. 代码质量 - 改进了代码组织结构 - 增强了错误处理 - 添加了详细的日志记录 - 优化了资源管理

6. 依赖管理 - 更新了 PyInstaller spec 文件 - 添加了必要的 PyQt5 依赖 - 确保打包后的程序正常运行
2025-02-12 13:33:11 +08:00
huangzhenpc
6281f86743 v3.2.6 稳定版本备份 2025-02-12 13:20:06 +08:00
15 changed files with 2313 additions and 385 deletions

View File

@@ -105,8 +105,9 @@ class AccountSwitcher:
Tuple[bool, str, Optional[Dict]]: (是否成功, 提示消息, 账号信息) Tuple[bool, str, Optional[Dict]]: (是否成功, 提示消息, 账号信息)
""" """
try: try:
# 获取当前状态 # 获取当前状态和历史记录
member_info = self.config.load_member_info() member_info = self.config.load_member_info()
activation_history = member_info.get("activation_records", []) if member_info else []
# 分割多个激活码 # 分割多个激活码
codes = [c.strip() for c in code.split(',')] codes = [c.strip() for c in code.split(',')]
@@ -182,16 +183,27 @@ class AccountSwitcher:
# 使用最后一次激活的结果作为最终状态 # 使用最后一次激活的结果作为最终状态
final_result = activation_results[-1] final_result = activation_results[-1]
# 保存会员信息 # 合并历史记录
new_activation_records = final_result.get("activation_records", [])
if activation_history:
# 保留旧的激活记录,避免重复
existing_codes = {record.get("code") for record in activation_history}
for record in new_activation_records:
if record.get("code") not in existing_codes:
activation_history.append(record)
else:
activation_history = new_activation_records
# 保存会员信息,包含完整的历史记录
member_info = { member_info = {
"hardware_id": final_result.get("machine_id", self.hardware_id), "hardware_id": final_result.get("machine_id", self.hardware_id),
"expire_time": final_result.get("expire_time", ""), "expire_time": final_result.get("expire_time", ""),
"days_left": final_result.get("days_left", 0), # 使用days_left "days_left": final_result.get("days_left", 0),
"total_days": final_result.get("total_days", 0), # 使用total_days "total_days": final_result.get("total_days", 0),
"status": final_result.get("status", "inactive"), "status": final_result.get("status", "inactive"),
"device_info": final_result.get("device_info", device_info), "device_info": final_result.get("device_info", device_info),
"activation_time": final_result.get("activation_time", ""), "activation_time": final_result.get("activation_time", ""),
"activation_records": final_result.get("activation_records", []) # 保存激活记录 "activation_records": activation_history # 使用合并后的历史记录
} }
self.config.save_member_info(member_info) self.config.save_member_info(member_info)
@@ -201,8 +213,7 @@ class AccountSwitcher:
# 显示每个成功激活码的信息 # 显示每个成功激活码的信息
for i, result in enumerate(activation_results, 1): for i, result in enumerate(activation_results, 1):
message += f"\n{i}个激活码:\n" message += f"\n{i}个激活码:\n"
message += f"- 新增天数: {result.get('added_days', 0)}\n" # 使用added_days显示本次新增天数 message += f"- 新增天数: {result.get('added_days', 0)}\n"
# 格式化时间显示
activation_time = result.get('activation_time', '') activation_time = result.get('activation_time', '')
if activation_time: if activation_time:
try: try:
@@ -214,8 +225,8 @@ class AccountSwitcher:
message += f"- 激活时间: {activation_time}\n" message += f"- 激活时间: {activation_time}\n"
message += f"\n最终状态:" message += f"\n最终状态:"
message += f"\n- 总天数: {final_result.get('total_days', 0)}" # 累计总天数 message += f"\n- 总天数: {final_result.get('total_days', 0)}"
message += f"\n- 剩余天数: {final_result.get('days_left', 0)}" # 剩余天数 message += f"\n- 剩余天数: {final_result.get('days_left', 0)}"
# 格式化到期时间显示 # 格式化到期时间显示
expire_time = final_result.get('expire_time', '') expire_time = final_result.get('expire_time', '')
@@ -227,9 +238,9 @@ class AccountSwitcher:
pass pass
message += f"\n- 到期时间: {expire_time}" message += f"\n- 到期时间: {expire_time}"
# 显示激活记录历史 # 显示完整的激活记录历史
message += "\n\n历史激活记录:" message += "\n\n历史激活记录:"
for record in final_result.get('activation_records', []): for record in activation_history:
activation_time = record.get('activation_time', '') activation_time = record.get('activation_time', '')
if activation_time: if activation_time:
try: try:
@@ -248,11 +259,11 @@ class AccountSwitcher:
except Exception as e: except Exception as e:
logging.error(f"处理激活结果时出错: {str(e)}") logging.error(f"处理激活结果时出错: {str(e)}")
return False, f"处理激活结果失败: {str(e)}", None return False, f"处理激活结果时出错: {str(e)}", None
except Exception as e: except Exception as e:
logging.error(f"激活码验证过程出错: {str(e)}") logging.error(f"验证激活码出错: {str(e)}")
return False, f"激活失败: {str(e)}", None return False, f"验证激活码时出错: {str(e)}", None
def reset_machine_id(self) -> bool: def reset_machine_id(self) -> bool:
"""重置机器码""" """重置机器码"""
@@ -262,7 +273,15 @@ class AccountSwitcher:
os.system("taskkill /f /im Cursor.exe >nul 2>&1") os.system("taskkill /f /im Cursor.exe >nul 2>&1")
time.sleep(2) time.sleep(2)
# 2. 删除 package.json 中的 machineId # 2. 清理注册表(包括更新系统 MachineGuid
if not self.registry.clean_registry():
logging.warning("注册表清理失败")
# 3. 清理文件(包括备份 storage.json
if not self.registry.clean_cursor_files():
logging.warning("文件清理失败")
# 4. 修改 package.json 中的 machineId
if self.package_json.exists(): if self.package_json.exists():
with open(self.package_json, "r", encoding="utf-8") as f: with open(self.package_json, "r", encoding="utf-8") as f:
data = json.load(f) data = json.load(f)
@@ -273,67 +292,20 @@ class AccountSwitcher:
with open(self.package_json, "w", encoding="utf-8") as f: with open(self.package_json, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2) json.dump(data, f, indent=2)
# 3. 清理特定的配置文件和缓存 # 5. 修改 storage.json 中的遥测 ID
local_app_data = Path(os.getenv('LOCALAPPDATA')) storage_path = Path(os.getenv('APPDATA')) / "Cursor" / "User" / "globalStorage" / "storage.json"
cursor_path = local_app_data / "Cursor" if storage_path.exists():
with open(storage_path, "r", encoding="utf-8") as f:
storage_data = json.load(f)
# 需要清理的目录 # 只修改 machineId保持其他遥测 ID 不变
cache_dirs = [ if "telemetry.machineId" in storage_data:
cursor_path / "Cache", # 生成新的 machineId使用与 GitHub 脚本类似的格式)
cursor_path / "Code Cache", new_machine_id = hashlib.sha256(str(uuid.uuid4()).encode()).hexdigest()
cursor_path / "GPUCache", storage_data["telemetry.machineId"] = new_machine_id
cursor_path / "Local Storage" / "leveldb"
]
# 需要删除的配置文件 with open(storage_path, "w", encoding="utf-8") as f:
config_files = [ json.dump(storage_data, f, indent=2)
cursor_path / "User" / "globalStorage" / "storage.json",
cursor_path / "User" / "globalStorage" / "state.json",
self.app_path / "config.json",
self.app_path / "state.json",
self.app_path / "settings.json"
]
# 清理缓存目录
for dir_path in cache_dirs:
if dir_path.exists():
try:
import shutil
shutil.rmtree(str(dir_path))
logging.info(f"清理目录成功: {dir_path}")
except Exception as e:
logging.warning(f"清理目录失败: {dir_path}, 错误: {str(e)}")
# 删除配置文件
for file_path in config_files:
if file_path.exists():
try:
os.remove(file_path)
logging.info(f"删除配置文件成功: {file_path}")
except Exception as e:
logging.warning(f"删除配置文件失败: {file_path}, 错误: {str(e)}")
# 4. 刷新注册表
if not self.registry.refresh_registry():
logging.warning("注册表刷新失败")
# 5. 删除卸载注册表项
try:
import winreg
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Uninstall", 0, winreg.KEY_ALL_ACCESS) as key:
# 遍历所有子键找到Cursor相关的
i = 0
while True:
try:
subkey_name = winreg.EnumKey(key, i)
if "Cursor" in subkey_name:
winreg.DeleteKey(key, subkey_name)
logging.info(f"删除注册表项成功: {subkey_name}")
i += 1
except WindowsError:
break
except Exception as e:
logging.warning(f"删除注册表项失败: {str(e)}")
# 6. 重启Cursor # 6. 重启Cursor
cursor_exe = self.cursor_path / "Cursor.exe" cursor_exe = self.cursor_path / "Cursor.exe"
@@ -523,7 +495,12 @@ class AccountSwitcher:
Tuple[bool, str]: (是否成功, 提示消息) Tuple[bool, str]: (是否成功, 提示消息)
""" """
try: try:
# 获取未使用的账号 # 1. 先关闭所有Cursor进程
if sys.platform == "win32":
os.system("taskkill /f /im Cursor.exe >nul 2>&1")
time.sleep(2)
# 2. 获取未使用的账号
endpoint = "https://cursorapi.nosqli.com/admin/api.account/getUnused" endpoint = "https://cursorapi.nosqli.com/admin/api.account/getUnused"
data = { data = {
"machine_id": self.hardware_id "machine_id": self.hardware_id
@@ -559,26 +536,14 @@ class AccountSwitcher:
if not all([email, access_token, refresh_token]): if not all([email, access_token, refresh_token]):
return False, "获取账号信息不完整" return False, "获取账号信息不完整"
# 更新Cursor认证信息 # 3. 更新Cursor认证信息
if not self.auth_manager.update_auth(email, access_token, refresh_token): if not self.auth_manager.update_auth(email, access_token, refresh_token):
return False, "更新Cursor认证信息失败" return False, "更新Cursor认证信息失败"
# 重置机器码 # 4. 重置机器码(包含了清理注册表、文件和重启操作)
if not self.reset_machine_id(): if not self.reset_machine_id():
return False, "重置机器码失败" return False, "重置机器码失败"
# 刷新注册表
if not self.registry.refresh_registry():
logging.warning("注册表刷新失败,但不影响主要功能")
# 重启Cursor
if not self.auth_manager.restart_cursor():
return False, "重启Cursor失败"
# 重启Cursor
# if not self.restart_cursor():
# logging.warning("Cursor重启失败请手动重启")
# return True, f"授权刷新成功请手动重启Cursor编辑器\n邮箱: {email}\n到期时间: {expire_time}\n剩余天数: {days_left}天"
return True, f"授权刷新成功Cursor编辑器已重启\n邮箱: {email}\n" return True, f"授权刷新成功Cursor编辑器已重启\n邮箱: {email}\n"
elif response_data.get("code") == 404: elif response_data.get("code") == 404:

202
after_github_reset.json Normal file
View File

@@ -0,0 +1,202 @@
{
"package_json": {
"C:\\Users\\huangzhen\\AppData\\Local\\Programs\\Cursor\\resources\\app\\package.json": {
"homepage": "https://cursor.so",
"author": {
"name": "Cursor AI, Inc.",
"email": "hiring@cursor.so"
},
"productName": "Cursor",
"description": "Cursor is an AI-first coding environment.",
"main": "./out/main.js",
"dependencies": {
"@todesktop/runtime": "=1.6.1",
"@electron/asar": "^3.2.3",
"@anysphere/file-service": "0.0.0-73d604b6",
"@microsoft/1ds-core-js": "^3.2.13",
"@microsoft/1ds-post-js": "^3.2.13",
"@parcel/watcher": "2.5.0",
"@sentry/electron": "5.7.0",
"@sentry/node": "8.35.0",
"@types/semver": "^7.5.8",
"@vscode/deviceid": "^0.1.1",
"@vscode/iconv-lite-umd": "0.7.0",
"@vscode/policy-watcher": "^1.1.8",
"@vscode/proxy-agent": "^0.27.0",
"@vscode/ripgrep": "^1.15.10",
"@vscode/spdlog": "^0.15.0",
"@vscode/sqlite3": "5.1.8-vscode",
"@vscode/sudo-prompt": "9.3.1",
"@vscode/tree-sitter-wasm": "^0.0.4",
"@vscode/vscode-languagedetection": "1.0.21",
"@vscode/windows-mutex": "^0.5.0",
"@vscode/windows-process-tree": "^0.6.0",
"@vscode/windows-registry": "^1.1.0",
"@xterm/addon-clipboard": "^0.2.0-beta.53",
"@xterm/addon-image": "^0.9.0-beta.70",
"@xterm/addon-ligatures": "^0.10.0-beta.70",
"@xterm/addon-search": "^0.16.0-beta.70",
"@xterm/addon-serialize": "^0.14.0-beta.70",
"@xterm/addon-unicode11": "^0.9.0-beta.70",
"@xterm/addon-webgl": "^0.19.0-beta.70",
"@xterm/headless": "^5.6.0-beta.70",
"@xterm/xterm": "^5.6.0-beta.70",
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.2",
"jschardet": "3.1.4",
"kerberos": "2.1.1",
"minimist": "^1.2.6",
"multiformats": "^13.3.1",
"native-is-elevated": "0.7.0",
"native-keymap": "^3.3.5",
"native-watchdog": "^1.4.1",
"node-fetch": "2.7.0",
"node-pty": "1.1.0-beta22",
"open": "^8.4.2",
"tas-client-umd": "0.2.0",
"v8-inspect-profiler": "^0.1.1",
"vscode-oniguruma": "1.7.0",
"vscode-regexpp": "^3.1.0",
"vscode-textmate": "9.1.0",
"yauzl": "^3.0.0",
"yazl": "^2.4.3"
},
"name": "cursor",
"version": "0.45.11",
"type": "module",
"desktopName": "cursor-url-handler.desktop",
"overrides": {},
"tdBuildId": "250207y6nbaw5qc",
"email": "jrxqnsoz250264@nosqli.com",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktTUEJLQlIxOTNKMjY3RENSVDRTR1YyIiwidGltZSI6IjE3MzkyNTAzNDgiLCJyYW5kb21uZXNzIjoiYWIyNWVhYTYtNDQzZC00Y2Q0IiwiZXhwIjo0MzMxMjUwMzQ4LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.f3VIttCJLWqhkEZpPmWJlYw32FuV_gLWl9E0N-O9oIc",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktTUEJLQlIxOTNKMjY3RENSVDRTR1YyIiwidGltZSI6IjE3MzkyNTAzNDgiLCJyYW5kb21uZXNzIjoiYWIyNWVhYTYtNDQzZC00Y2Q0IiwiZXhwIjo0MzMxMjUwMzQ4LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.f3VIttCJLWqhkEZpPmWJlYw32FuV_gLWl9E0N-O9oIc"
}
},
"storage_json": {
"telemetry.machineId": "758a7f7f7f79078f9f2c690514878ea3e8f064c0a49e837dd396db89df58429c",
"telemetry.macMachineId": "ff2a4a580f6e9e484c830204bb502866e9a333d3e0299ef81c34e01940da953e",
"telemetry.sqmId": "{D73E6881-666C-4182-8CB2-E2A3EED5AEFF}",
"telemetry.devDeviceId": "1ae7f91c-3ab8-448c-bbd3-ef34345a5b05",
"backupWorkspaces": {
"workspaces": [],
"folders": [
{
"folderUri": "file:///d%3A/W/python/001cursro.app/interactive"
}
],
"emptyWindows": [
{
"backupFolder": "1739332115293"
}
]
},
"windowControlHeight": 35,
"profileAssociations": {
"workspaces": {
"file:///d%3A/W/python/001cursro.app/interactive": "__default__profile__"
},
"emptyWindows": {
"1739332115293": "__default__profile__"
}
},
"theme": "vs-dark",
"themeBackground": "#1f1f1f",
"windowSplash": {
"zoomLevel": 0,
"baseTheme": "vs-dark",
"colorInfo": {
"foreground": "#cccccc",
"background": "#1f1f1f",
"editorBackground": "#1f1f1f",
"titleBarBackground": "#181818",
"titleBarBorder": "#2b2b2b",
"activityBarBackground": "#181818",
"activityBarBorder": "#2b2b2b",
"sideBarBackground": "#181818",
"sideBarBorder": "#2b2b2b",
"statusBarBackground": "#181818",
"statusBarBorder": "#2b2b2b",
"statusBarNoFolderBackground": "#1f1f1f"
},
"layoutInfo": {
"sideBarSide": "left",
"editorPartMinWidth": 220,
"titleBarHeight": 35,
"activityBarWidth": 0,
"sideBarWidth": 170,
"statusBarHeight": 22,
"windowBorder": false
}
}
},
"registry": {
"HKLM_MachineGuid": {
"exists": true,
"value": "776c6b6c-195f-42dc-94d6-72b70c3aca74"
},
"HKCU_cursor_shell": {
"exists": false,
"values": {}
},
"HKCU_cursor_command": {
"exists": false,
"values": {}
},
"HKCU_cursor_auth": {
"exists": false,
"values": {}
},
"HKCU_cursor_updates": {
"exists": false,
"values": {}
},
"HKCU_cursor_main": {
"exists": false,
"values": {}
}
},
"files": {
"storage": {
"exists": true,
"is_dir": false,
"size": 1891,
"modified_time": "2025-02-12T11:48:42.627574"
},
"storage_backup": {
"exists": true,
"is_dir": true,
"size": null,
"modified_time": "2025-02-12T11:48:26.403770"
},
"user_data": {
"exists": false,
"is_dir": null,
"size": null,
"modified_time": null
},
"global_storage": {
"exists": true,
"is_dir": true,
"size": null,
"modified_time": "2025-02-12T11:48:47.490659"
},
"cache": {
"exists": false,
"is_dir": null,
"size": null,
"modified_time": null
},
"updater": {
"exists": true,
"is_dir": false,
"size": 0,
"modified_time": "2025-02-10T17:19:39.071580"
}
},
"telemetry": {
"machineId": "758a7f7f7f79078f9f2c690514878ea3e8f064c0a49e837dd396db89df58429c",
"macMachineId": "ff2a4a580f6e9e484c830204bb502866e9a333d3e0299ef81c34e01940da953e",
"devDeviceId": "1ae7f91c-3ab8-448c-bbd3-ef34345a5b05",
"sqmId": "{D73E6881-666C-4182-8CB2-E2A3EED5AEFF}"
}
}

216
after_our_reset.json Normal file
View File

@@ -0,0 +1,216 @@
{
"package_json": {
"C:\\Users\\huangzhen\\AppData\\Local\\Programs\\Cursor\\resources\\app\\package.json": {
"homepage": "https://cursor.so",
"author": {
"name": "Cursor AI, Inc.",
"email": "hiring@cursor.so"
},
"productName": "Cursor",
"description": "Cursor is an AI-first coding environment.",
"main": "./out/main.js",
"dependencies": {
"@todesktop/runtime": "=1.6.1",
"@electron/asar": "^3.2.3",
"@anysphere/file-service": "0.0.0-73d604b6",
"@microsoft/1ds-core-js": "^3.2.13",
"@microsoft/1ds-post-js": "^3.2.13",
"@parcel/watcher": "2.5.0",
"@sentry/electron": "5.7.0",
"@sentry/node": "8.35.0",
"@types/semver": "^7.5.8",
"@vscode/deviceid": "^0.1.1",
"@vscode/iconv-lite-umd": "0.7.0",
"@vscode/policy-watcher": "^1.1.8",
"@vscode/proxy-agent": "^0.27.0",
"@vscode/ripgrep": "^1.15.10",
"@vscode/spdlog": "^0.15.0",
"@vscode/sqlite3": "5.1.8-vscode",
"@vscode/sudo-prompt": "9.3.1",
"@vscode/tree-sitter-wasm": "^0.0.4",
"@vscode/vscode-languagedetection": "1.0.21",
"@vscode/windows-mutex": "^0.5.0",
"@vscode/windows-process-tree": "^0.6.0",
"@vscode/windows-registry": "^1.1.0",
"@xterm/addon-clipboard": "^0.2.0-beta.53",
"@xterm/addon-image": "^0.9.0-beta.70",
"@xterm/addon-ligatures": "^0.10.0-beta.70",
"@xterm/addon-search": "^0.16.0-beta.70",
"@xterm/addon-serialize": "^0.14.0-beta.70",
"@xterm/addon-unicode11": "^0.9.0-beta.70",
"@xterm/addon-webgl": "^0.19.0-beta.70",
"@xterm/headless": "^5.6.0-beta.70",
"@xterm/xterm": "^5.6.0-beta.70",
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.2",
"jschardet": "3.1.4",
"kerberos": "2.1.1",
"minimist": "^1.2.6",
"multiformats": "^13.3.1",
"native-is-elevated": "0.7.0",
"native-keymap": "^3.3.5",
"native-watchdog": "^1.4.1",
"node-fetch": "2.7.0",
"node-pty": "1.1.0-beta22",
"open": "^8.4.2",
"tas-client-umd": "0.2.0",
"v8-inspect-profiler": "^0.1.1",
"vscode-oniguruma": "1.7.0",
"vscode-regexpp": "^3.1.0",
"vscode-textmate": "9.1.0",
"yauzl": "^3.0.0",
"yazl": "^2.4.3"
},
"name": "cursor",
"version": "0.45.11",
"type": "module",
"desktopName": "cursor-url-handler.desktop",
"overrides": {},
"tdBuildId": "250207y6nbaw5qc",
"email": "jrxqnsoz250264@nosqli.com",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktTUEJLQlIxOTNKMjY3RENSVDRTR1YyIiwidGltZSI6IjE3MzkyNTAzNDgiLCJyYW5kb21uZXNzIjoiYWIyNWVhYTYtNDQzZC00Y2Q0IiwiZXhwIjo0MzMxMjUwMzQ4LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.f3VIttCJLWqhkEZpPmWJlYw32FuV_gLWl9E0N-O9oIc",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktTUEJLQlIxOTNKMjY3RENSVDRTR1YyIiwidGltZSI6IjE3MzkyNTAzNDgiLCJyYW5kb21uZXNzIjoiYWIyNWVhYTYtNDQzZC00Y2Q0IiwiZXhwIjo0MzMxMjUwMzQ4LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.f3VIttCJLWqhkEZpPmWJlYw32FuV_gLWl9E0N-O9oIc",
"updateUrl": "",
"disableUpdate": true,
"enableNodeApiUncaughtExceptionPolicy": true,
"nodeOptions": [
"--force-node-api-uncaught-exceptions-policy=true"
]
}
},
"storage_json": {
"telemetry.machineId": "b0cbb2d13ca4c983be40d31e010819f16adb3d6083598f1457094837bdaa3def",
"telemetry.macMachineId": "ff2a4a580f6e9e484c830204bb502866e9a333d3e0299ef81c34e01940da953e",
"telemetry.sqmId": "{D73E6881-666C-4182-8CB2-E2A3EED5AEFF}",
"telemetry.devDeviceId": "1ae7f91c-3ab8-448c-bbd3-ef34345a5b05",
"backupWorkspaces": {
"workspaces": [],
"folders": [
{
"folderUri": "file:///d%3A/W/python/001cursro.app/interactive"
}
],
"emptyWindows": []
},
"windowControlHeight": 35,
"profileAssociations": {
"workspaces": {
"file:///d%3A/W/python/001cursro.app/interactive": "__default__profile__"
},
"emptyWindows": {}
},
"theme": "vs-dark",
"themeBackground": "#1f1f1f",
"windowSplash": {
"zoomLevel": 0,
"baseTheme": "vs-dark",
"colorInfo": {
"foreground": "#cccccc",
"background": "#1f1f1f",
"editorBackground": "#1f1f1f",
"titleBarBackground": "#181818",
"titleBarBorder": "#2b2b2b",
"activityBarBackground": "#181818",
"activityBarBorder": "#2b2b2b",
"sideBarBackground": "#181818",
"sideBarBorder": "#2b2b2b",
"statusBarBackground": "#181818",
"statusBarBorder": "#2b2b2b",
"statusBarNoFolderBackground": "#1f1f1f"
},
"layoutInfo": {
"sideBarSide": "left",
"editorPartMinWidth": 220,
"titleBarHeight": 35,
"activityBarWidth": 0,
"sideBarWidth": 300,
"statusBarHeight": 22,
"windowBorder": false
}
},
"windowsState": {
"lastActiveWindow": {
"folder": "file:///d%3A/W/python/001cursro.app/interactive",
"backupPath": "C:\\Users\\huangzhen\\AppData\\Roaming\\Cursor\\Backups\\385f155a4a13070be99ee4e76a057235",
"uiState": {
"mode": 0,
"x": 512,
"y": 192,
"width": 1024,
"height": 768
}
},
"openedWindows": []
}
},
"registry": {
"HKLM_MachineGuid": {
"exists": true,
"value": "1deb25e7-cdd4-4367-a347-fba8b33b9b03"
},
"HKCU_cursor_shell": {
"exists": false,
"values": {}
},
"HKCU_cursor_command": {
"exists": false,
"values": {}
},
"HKCU_cursor_auth": {
"exists": false,
"values": {}
},
"HKCU_cursor_updates": {
"exists": false,
"values": {}
},
"HKCU_cursor_main": {
"exists": false,
"values": {}
}
},
"files": {
"storage": {
"exists": true,
"is_dir": false,
"size": 1980,
"modified_time": "2025-02-12T12:37:17.428609"
},
"storage_backup": {
"exists": true,
"is_dir": true,
"size": null,
"modified_time": "2025-02-12T12:37:17.371311"
},
"user_data": {
"exists": false,
"is_dir": null,
"size": null,
"modified_time": null
},
"global_storage": {
"exists": true,
"is_dir": true,
"size": null,
"modified_time": "2025-02-12T12:37:14.253083"
},
"cache": {
"exists": false,
"is_dir": null,
"size": null,
"modified_time": null
},
"updater": {
"exists": true,
"is_dir": false,
"size": 0,
"modified_time": "2025-02-10T17:19:39.071580"
}
},
"telemetry": {
"machineId": "b0cbb2d13ca4c983be40d31e010819f16adb3d6083598f1457094837bdaa3def",
"macMachineId": "ff2a4a580f6e9e484c830204bb502866e9a333d3e0299ef81c34e01940da953e",
"devDeviceId": "1ae7f91c-3ab8-448c-bbd3-ef34345a5b05",
"sqmId": "{D73E6881-666C-4182-8CB2-E2A3EED5AEFF}"
}
}

244
before_github_reset.json Normal file
View File

@@ -0,0 +1,244 @@
{
"package_json": {
"C:\\Users\\huangzhen\\AppData\\Local\\Programs\\Cursor\\resources\\app\\package.json": {
"homepage": "https://cursor.so",
"author": {
"name": "Cursor AI, Inc.",
"email": "hiring@cursor.so"
},
"productName": "Cursor",
"description": "Cursor is an AI-first coding environment.",
"main": "./out/main.js",
"dependencies": {
"@todesktop/runtime": "=1.6.1",
"@electron/asar": "^3.2.3",
"@anysphere/file-service": "0.0.0-73d604b6",
"@microsoft/1ds-core-js": "^3.2.13",
"@microsoft/1ds-post-js": "^3.2.13",
"@parcel/watcher": "2.5.0",
"@sentry/electron": "5.7.0",
"@sentry/node": "8.35.0",
"@types/semver": "^7.5.8",
"@vscode/deviceid": "^0.1.1",
"@vscode/iconv-lite-umd": "0.7.0",
"@vscode/policy-watcher": "^1.1.8",
"@vscode/proxy-agent": "^0.27.0",
"@vscode/ripgrep": "^1.15.10",
"@vscode/spdlog": "^0.15.0",
"@vscode/sqlite3": "5.1.8-vscode",
"@vscode/sudo-prompt": "9.3.1",
"@vscode/tree-sitter-wasm": "^0.0.4",
"@vscode/vscode-languagedetection": "1.0.21",
"@vscode/windows-mutex": "^0.5.0",
"@vscode/windows-process-tree": "^0.6.0",
"@vscode/windows-registry": "^1.1.0",
"@xterm/addon-clipboard": "^0.2.0-beta.53",
"@xterm/addon-image": "^0.9.0-beta.70",
"@xterm/addon-ligatures": "^0.10.0-beta.70",
"@xterm/addon-search": "^0.16.0-beta.70",
"@xterm/addon-serialize": "^0.14.0-beta.70",
"@xterm/addon-unicode11": "^0.9.0-beta.70",
"@xterm/addon-webgl": "^0.19.0-beta.70",
"@xterm/headless": "^5.6.0-beta.70",
"@xterm/xterm": "^5.6.0-beta.70",
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.2",
"jschardet": "3.1.4",
"kerberos": "2.1.1",
"minimist": "^1.2.6",
"multiformats": "^13.3.1",
"native-is-elevated": "0.7.0",
"native-keymap": "^3.3.5",
"native-watchdog": "^1.4.1",
"node-fetch": "2.7.0",
"node-pty": "1.1.0-beta22",
"open": "^8.4.2",
"tas-client-umd": "0.2.0",
"v8-inspect-profiler": "^0.1.1",
"vscode-oniguruma": "1.7.0",
"vscode-regexpp": "^3.1.0",
"vscode-textmate": "9.1.0",
"yauzl": "^3.0.0",
"yazl": "^2.4.3"
},
"name": "cursor",
"version": "0.45.11",
"type": "module",
"desktopName": "cursor-url-handler.desktop",
"overrides": {},
"tdBuildId": "250207y6nbaw5qc",
"email": "jrxqnsoz250264@nosqli.com",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktTUEJLQlIxOTNKMjY3RENSVDRTR1YyIiwidGltZSI6IjE3MzkyNTAzNDgiLCJyYW5kb21uZXNzIjoiYWIyNWVhYTYtNDQzZC00Y2Q0IiwiZXhwIjo0MzMxMjUwMzQ4LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.f3VIttCJLWqhkEZpPmWJlYw32FuV_gLWl9E0N-O9oIc",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktTUEJLQlIxOTNKMjY3RENSVDRTR1YyIiwidGltZSI6IjE3MzkyNTAzNDgiLCJyYW5kb21uZXNzIjoiYWIyNWVhYTYtNDQzZC00Y2Q0IiwiZXhwIjo0MzMxMjUwMzQ4LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.f3VIttCJLWqhkEZpPmWJlYw32FuV_gLWl9E0N-O9oIc"
}
},
"storage_json": {
"telemetry.machineId": "31b3701f1790cdb754bd8a02bad4913a9f8a3f04c9e19c519996be8c7b8cb561",
"telemetry.macMachineId": "ff2a4a580f6e9e484c830204bb502866e9a333d3e0299ef81c34e01940da953e",
"telemetry.sqmId": "{D73E6881-666C-4182-8CB2-E2A3EED5AEFF}",
"telemetry.devDeviceId": "1ae7f91c-3ab8-448c-bbd3-ef34345a5b05",
"backupWorkspaces": {
"workspaces": [],
"folders": [
{
"folderUri": "file:///d%3A/W/python/001cursro.app/interactive"
},
{
"folderUri": "file:///d%3A/W/python/003cursorapiadmin"
}
],
"emptyWindows": [
{
"backupFolder": "1739329218500"
},
{
"backupFolder": "1739329245089"
}
]
},
"windowControlHeight": 35,
"profileAssociations": {
"workspaces": {
"file:///d%3A/W/python/001cursro.app/interactive": "__default__profile__",
"file:///d%3A/W/python/003cursorapiadmin": "__default__profile__"
},
"emptyWindows": {}
},
"theme": "vs-dark",
"themeBackground": "#1f1f1f",
"windowSplash": {
"zoomLevel": 0,
"baseTheme": "vs-dark",
"colorInfo": {
"foreground": "#cccccc",
"background": "#1f1f1f",
"editorBackground": "#1f1f1f",
"titleBarBackground": "#181818",
"titleBarBorder": "#2b2b2b",
"activityBarBackground": "#181818",
"activityBarBorder": "#2b2b2b",
"sideBarBackground": "#181818",
"sideBarBorder": "#2b2b2b",
"statusBarBackground": "#181818",
"statusBarBorder": "#2b2b2b",
"statusBarNoFolderBackground": "#1f1f1f"
},
"layoutInfo": {
"sideBarSide": "left",
"editorPartMinWidth": 220,
"titleBarHeight": 35,
"activityBarWidth": 0,
"sideBarWidth": 170,
"statusBarHeight": 22,
"windowBorder": false
}
},
"windowsState": {
"lastActiveWindow": {
"folder": "file:///d%3A/W/python/001cursro.app/interactive",
"backupPath": "C:\\Users\\huangzhen\\AppData\\Roaming\\Cursor\\Backups\\385f155a4a13070be99ee4e76a057235",
"uiState": {
"mode": 0,
"x": 512,
"y": 192,
"width": 1024,
"height": 768
}
},
"openedWindows": [
{
"folder": "file:///d%3A/W/python/001cursro.app/interactive",
"backupPath": "C:\\Users\\huangzhen\\AppData\\Roaming\\Cursor\\Backups\\385f155a4a13070be99ee4e76a057235",
"uiState": {
"mode": 0,
"x": 512,
"y": 192,
"width": 1024,
"height": 768
}
},
{
"folder": "file:///d%3A/W/python/003cursorapiadmin",
"backupPath": "C:\\Users\\huangzhen\\AppData\\Roaming\\Cursor\\Backups\\b6b8cfb24ed2ddb05d90d45cce5443e7",
"uiState": {
"mode": 0,
"x": 512,
"y": 192,
"width": 1024,
"height": 768
}
}
]
}
},
"registry": {
"HKLM_MachineGuid": {
"exists": true,
"value": "d890ab3d-43cd-40c8-a9ef-f5683b5a64e3"
},
"HKCU_cursor_shell": {
"exists": false,
"values": {}
},
"HKCU_cursor_command": {
"exists": false,
"values": {}
},
"HKCU_cursor_auth": {
"exists": false,
"values": {}
},
"HKCU_cursor_updates": {
"exists": false,
"values": {}
},
"HKCU_cursor_main": {
"exists": false,
"values": {}
}
},
"files": {
"storage": {
"exists": true,
"is_dir": false,
"size": 3427,
"modified_time": "2025-02-12T11:40:57.046415"
},
"storage_backup": {
"exists": true,
"is_dir": true,
"size": null,
"modified_time": "2025-02-12T11:00:01.555876"
},
"user_data": {
"exists": false,
"is_dir": null,
"size": null,
"modified_time": null
},
"global_storage": {
"exists": true,
"is_dir": true,
"size": null,
"modified_time": "2025-02-12T11:47:27.627883"
},
"cache": {
"exists": false,
"is_dir": null,
"size": null,
"modified_time": null
},
"updater": {
"exists": true,
"is_dir": false,
"size": 0,
"modified_time": "2025-02-10T17:19:39.071580"
}
},
"telemetry": {
"machineId": "31b3701f1790cdb754bd8a02bad4913a9f8a3f04c9e19c519996be8c7b8cb561",
"macMachineId": "ff2a4a580f6e9e484c830204bb502866e9a333d3e0299ef81c34e01940da953e",
"devDeviceId": "1ae7f91c-3ab8-448c-bbd3-ef34345a5b05",
"sqmId": "{D73E6881-666C-4182-8CB2-E2A3EED5AEFF}"
}
}

216
before_our_reset.json Normal file
View File

@@ -0,0 +1,216 @@
{
"package_json": {
"C:\\Users\\huangzhen\\AppData\\Local\\Programs\\Cursor\\resources\\app\\package.json": {
"homepage": "https://cursor.so",
"author": {
"name": "Cursor AI, Inc.",
"email": "hiring@cursor.so"
},
"productName": "Cursor",
"description": "Cursor is an AI-first coding environment.",
"main": "./out/main.js",
"dependencies": {
"@todesktop/runtime": "=1.6.1",
"@electron/asar": "^3.2.3",
"@anysphere/file-service": "0.0.0-73d604b6",
"@microsoft/1ds-core-js": "^3.2.13",
"@microsoft/1ds-post-js": "^3.2.13",
"@parcel/watcher": "2.5.0",
"@sentry/electron": "5.7.0",
"@sentry/node": "8.35.0",
"@types/semver": "^7.5.8",
"@vscode/deviceid": "^0.1.1",
"@vscode/iconv-lite-umd": "0.7.0",
"@vscode/policy-watcher": "^1.1.8",
"@vscode/proxy-agent": "^0.27.0",
"@vscode/ripgrep": "^1.15.10",
"@vscode/spdlog": "^0.15.0",
"@vscode/sqlite3": "5.1.8-vscode",
"@vscode/sudo-prompt": "9.3.1",
"@vscode/tree-sitter-wasm": "^0.0.4",
"@vscode/vscode-languagedetection": "1.0.21",
"@vscode/windows-mutex": "^0.5.0",
"@vscode/windows-process-tree": "^0.6.0",
"@vscode/windows-registry": "^1.1.0",
"@xterm/addon-clipboard": "^0.2.0-beta.53",
"@xterm/addon-image": "^0.9.0-beta.70",
"@xterm/addon-ligatures": "^0.10.0-beta.70",
"@xterm/addon-search": "^0.16.0-beta.70",
"@xterm/addon-serialize": "^0.14.0-beta.70",
"@xterm/addon-unicode11": "^0.9.0-beta.70",
"@xterm/addon-webgl": "^0.19.0-beta.70",
"@xterm/headless": "^5.6.0-beta.70",
"@xterm/xterm": "^5.6.0-beta.70",
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.2",
"jschardet": "3.1.4",
"kerberos": "2.1.1",
"minimist": "^1.2.6",
"multiformats": "^13.3.1",
"native-is-elevated": "0.7.0",
"native-keymap": "^3.3.5",
"native-watchdog": "^1.4.1",
"node-fetch": "2.7.0",
"node-pty": "1.1.0-beta22",
"open": "^8.4.2",
"tas-client-umd": "0.2.0",
"v8-inspect-profiler": "^0.1.1",
"vscode-oniguruma": "1.7.0",
"vscode-regexpp": "^3.1.0",
"vscode-textmate": "9.1.0",
"yauzl": "^3.0.0",
"yazl": "^2.4.3"
},
"name": "cursor",
"version": "0.45.11",
"type": "module",
"desktopName": "cursor-url-handler.desktop",
"overrides": {},
"tdBuildId": "250207y6nbaw5qc",
"email": "jrxqnsoz250264@nosqli.com",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktTUEJLQlIxOTNKMjY3RENSVDRTR1YyIiwidGltZSI6IjE3MzkyNTAzNDgiLCJyYW5kb21uZXNzIjoiYWIyNWVhYTYtNDQzZC00Y2Q0IiwiZXhwIjo0MzMxMjUwMzQ4LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.f3VIttCJLWqhkEZpPmWJlYw32FuV_gLWl9E0N-O9oIc",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktTUEJLQlIxOTNKMjY3RENSVDRTR1YyIiwidGltZSI6IjE3MzkyNTAzNDgiLCJyYW5kb21uZXNzIjoiYWIyNWVhYTYtNDQzZC00Y2Q0IiwiZXhwIjo0MzMxMjUwMzQ4LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.f3VIttCJLWqhkEZpPmWJlYw32FuV_gLWl9E0N-O9oIc",
"updateUrl": "",
"disableUpdate": true,
"enableNodeApiUncaughtExceptionPolicy": true,
"nodeOptions": [
"--force-node-api-uncaught-exceptions-policy=true"
]
}
},
"storage_json": {
"telemetry.machineId": "9eb67b11924f32572a67e6480ce4f1cabf3f61503aa4918af506b259527a4745",
"telemetry.macMachineId": "ff2a4a580f6e9e484c830204bb502866e9a333d3e0299ef81c34e01940da953e",
"telemetry.sqmId": "{D73E6881-666C-4182-8CB2-E2A3EED5AEFF}",
"telemetry.devDeviceId": "1ae7f91c-3ab8-448c-bbd3-ef34345a5b05",
"backupWorkspaces": {
"workspaces": [],
"folders": [
{
"folderUri": "file:///d%3A/W/python/001cursro.app/interactive"
}
],
"emptyWindows": []
},
"windowControlHeight": 35,
"profileAssociations": {
"workspaces": {
"file:///d%3A/W/python/001cursro.app/interactive": "__default__profile__"
},
"emptyWindows": {}
},
"theme": "vs-dark",
"themeBackground": "#1f1f1f",
"windowSplash": {
"zoomLevel": 0,
"baseTheme": "vs-dark",
"colorInfo": {
"foreground": "#cccccc",
"background": "#1f1f1f",
"editorBackground": "#1f1f1f",
"titleBarBackground": "#181818",
"titleBarBorder": "#2b2b2b",
"activityBarBackground": "#181818",
"activityBarBorder": "#2b2b2b",
"sideBarBackground": "#181818",
"sideBarBorder": "#2b2b2b",
"statusBarBackground": "#181818",
"statusBarBorder": "#2b2b2b",
"statusBarNoFolderBackground": "#1f1f1f"
},
"layoutInfo": {
"sideBarSide": "left",
"editorPartMinWidth": 220,
"titleBarHeight": 35,
"activityBarWidth": 0,
"sideBarWidth": 300,
"statusBarHeight": 22,
"windowBorder": false
}
},
"windowsState": {
"lastActiveWindow": {
"folder": "file:///d%3A/W/python/001cursro.app/interactive",
"backupPath": "C:\\Users\\huangzhen\\AppData\\Roaming\\Cursor\\Backups\\385f155a4a13070be99ee4e76a057235",
"uiState": {
"mode": 0,
"x": 512,
"y": 192,
"width": 1024,
"height": 768
}
},
"openedWindows": []
}
},
"registry": {
"HKLM_MachineGuid": {
"exists": true,
"value": "948b93c1-ee34-4a48-95d0-a2fce9af92b1"
},
"HKCU_cursor_shell": {
"exists": false,
"values": {}
},
"HKCU_cursor_command": {
"exists": false,
"values": {}
},
"HKCU_cursor_auth": {
"exists": false,
"values": {}
},
"HKCU_cursor_updates": {
"exists": false,
"values": {}
},
"HKCU_cursor_main": {
"exists": false,
"values": {}
}
},
"files": {
"storage": {
"exists": true,
"is_dir": false,
"size": 2214,
"modified_time": "2025-02-12T12:36:27.187387"
},
"storage_backup": {
"exists": true,
"is_dir": true,
"size": null,
"modified_time": "2025-02-12T12:33:10.408749"
},
"user_data": {
"exists": false,
"is_dir": null,
"size": null,
"modified_time": null
},
"global_storage": {
"exists": true,
"is_dir": true,
"size": null,
"modified_time": "2025-02-12T12:37:14.253083"
},
"cache": {
"exists": false,
"is_dir": null,
"size": null,
"modified_time": null
},
"updater": {
"exists": true,
"is_dir": false,
"size": 0,
"modified_time": "2025-02-10T17:19:39.071580"
}
},
"telemetry": {
"machineId": "9eb67b11924f32572a67e6480ce4f1cabf3f61503aa4918af506b259527a4745",
"macMachineId": "ff2a4a580f6e9e484c830204bb502866e9a333d3e0299ef81c34e01940da953e",
"devDeviceId": "1ae7f91c-3ab8-448c-bbd3-ef34345a5b05",
"sqmId": "{D73E6881-666C-4182-8CB2-E2A3EED5AEFF}"
}
}

View File

@@ -13,7 +13,12 @@ a = Analysis(
pathex=[], pathex=[],
binaries=[], binaries=[],
datas=[('icon', 'icon'), ('version.txt', '.')], datas=[('icon', 'icon'), ('version.txt', '.')],
hiddenimports=[], hiddenimports=[
'win32gui', 'win32con', 'win32process', 'psutil', # Windows API 相关
'PyQt5', 'PyQt5.QtCore', 'PyQt5.QtGui', 'PyQt5.QtWidgets', # GUI 相关
'requests', 'urllib3', 'certifi', # 网络请求相关
'json', 'uuid', 'hashlib', 'logging' # 基础功能相关
],
hookspath=[], hookspath=[],
hooksconfig={}, hooksconfig={},
runtime_hooks=[], runtime_hooks=[],

504
cursor_win_id_modifier.ps1 Normal file
View File

@@ -0,0 +1,504 @@
# 设置输出编码为 UTF-8
$OutputEncoding = [System.Text.Encoding]::UTF8
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
# 颜色定义
$RED = "`e[31m"
$GREEN = "`e[32m"
$YELLOW = "`e[33m"
$BLUE = "`e[34m"
$NC = "`e[0m"
# 配置文件路径
$STORAGE_FILE = "$env:APPDATA\Cursor\User\globalStorage\storage.json"
$BACKUP_DIR = "$env:APPDATA\Cursor\User\globalStorage\backups"
# 检查管理员权限
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 设备ID 修改工具 $NC"
Write-Host "$YELLOW 关注公众号【煎饼果子卷AI】 $NC"
Write-Host "$YELLOW 一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) $NC"
Write-Host "$YELLOW [重要提示] 本工具免费如果对您有帮助请关注公众号【煎饼果子卷AI】 $NC"
Write-Host "$BLUE================================$NC"
Write-Host ""
# 获取并显示 Cursor 版本
function Get-CursorVersion {
try {
# 主要检测路径
$packagePath = "$env:LOCALAPPDATA\Programs\cursor\resources\app\package.json"
if (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 = "$env:LOCALAPPDATA\cursor\resources\app\package.json"
if (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 最新的 0.45.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-CursorProcess {
param($processName)
$process = Get-Process -Name $processName -ErrorAction SilentlyContinue
if ($process) {
Write-Host "$YELLOW[警告]$NC 发现 $processName 正在运行"
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 已成功关闭"
}
}
# 关闭所有 Cursor 进程
Close-CursorProcess "Cursor"
Close-CursorProcess "cursor"
# 创建备份目录
if (-not (Test-Path $BACKUP_DIR)) {
New-Item -ItemType Directory -Path $BACKUP_DIR | Out-Null
}
# 备份现有配置
if (Test-Path $STORAGE_FILE) {
Write-Host "$GREEN[信息]$NC 正在备份配置文件..."
$backupName = "storage.json.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
Copy-Item $STORAGE_FILE "$BACKUP_DIR\$backupName"
}
# 生成新的 ID
Write-Host "$GREEN[信息]$NC 正在生成新的 ID..."
# 在颜色定义后添加此函数
function Get-RandomHex {
param (
[int]$length
)
$bytes = New-Object byte[] ($length)
$rng = [System.Security.Cryptography.RNGCryptoServiceProvider]::new()
$rng.GetBytes($bytes)
$hexString = [System.BitConverter]::ToString($bytes) -replace '-',''
$rng.Dispose()
return $hexString
}
# 改进 ID 生成函数
function New-StandardMachineId {
$template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
$result = $template -replace '[xy]', {
param($match)
$r = [Random]::new().Next(16)
$v = if ($match.Value -eq "x") { $r } else { ($r -band 0x3) -bor 0x8 }
return $v.ToString("x")
}
return $result
}
# 在生成 ID 时使用新函数
$MAC_MACHINE_ID = New-StandardMachineId
$UUID = [System.Guid]::NewGuid().ToString()
# 将 auth0|user_ 转换为字节数组的十六进制
$prefixBytes = [System.Text.Encoding]::UTF8.GetBytes("auth0|user_")
$prefixHex = -join ($prefixBytes | ForEach-Object { '{0:x2}' -f $_ })
# 生成32字节(64个十六进制字符)的随机数作为 machineId 的随机部分
$randomPart = Get-RandomHex -length 32
$MACHINE_ID = "$prefixHex$randomPart"
$SQM_ID = "{$([System.Guid]::NewGuid().ToString().ToUpper())}"
# 在生成新ID后直接执行注册表操作移除询问
function Update-MachineGuid {
try {
$newMachineGuid = [System.Guid]::NewGuid().ToString()
$registryPath = "HKLM:\SOFTWARE\Microsoft\Cryptography"
# 备份原始值
$originalGuid = (Get-ItemProperty -Path $registryPath -Name "MachineGuid").MachineGuid
$backupFile = "$BACKUP_DIR\MachineGuid.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
$originalGuid | Out-File $backupFile -Encoding UTF8
# 更新注册表
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"
}
catch {
Write-Host "$RED[错误]$NC 更新系统 MachineGuid 失败: $_"
}
}
# 创建或更新配置文件
Write-Host "$GREEN[信息]$NC 正在更新配置..."
try {
# 检查配置文件是否存在
if (-not (Test-Path $STORAGE_FILE)) {
Write-Host "$RED[错误]$NC 未找到配置文件: $STORAGE_FILE"
Write-Host "$YELLOW[提示]$NC 请先安装并运行一次 Cursor 后再使用此脚本"
Read-Host "按回车键退出"
exit 1
}
# 读取现有配置文件
try {
$originalContent = Get-Content $STORAGE_FILE -Raw -Encoding UTF8
# 将 JSON 字符串转换为 PowerShell 对象
$config = $originalContent | ConvertFrom-Json
# 备份当前值
$oldValues = @{
'machineId' = $config.'telemetry.machineId'
'macMachineId' = $config.'telemetry.macMachineId'
'devDeviceId' = $config.'telemetry.devDeviceId'
'sqmId' = $config.'telemetry.sqmId'
}
# 更新特定的值
$config.'telemetry.machineId' = $MACHINE_ID
$config.'telemetry.macMachineId' = $MAC_MACHINE_ID
$config.'telemetry.devDeviceId' = $UUID
$config.'telemetry.sqmId' = $SQM_ID
# 将更新后的对象转换回 JSON 并保存
$updatedJson = $config | ConvertTo-Json -Depth 10
[System.IO.File]::WriteAllText(
[System.IO.Path]::GetFullPath($STORAGE_FILE),
$updatedJson,
[System.Text.Encoding]::UTF8
)
Write-Host "$GREEN[信息]$NC 成功更新配置文件"
} catch {
# 如果出错,尝试恢复原始内容
if ($originalContent) {
[System.IO.File]::WriteAllText(
[System.IO.Path]::GetFullPath($STORAGE_FILE),
$originalContent,
[System.Text.Encoding]::UTF8
)
}
throw "处理 JSON 失败: $_"
}
# 直接执行更新 MachineGuid不再询问
Update-MachineGuid
# 显示结果
Write-Host ""
Write-Host "$GREEN[信息]$NC 已更新配置:"
Write-Host "$BLUE[调试]$NC machineId: $MACHINE_ID"
Write-Host "$BLUE[调试]$NC macMachineId: $MAC_MACHINE_ID"
Write-Host "$BLUE[调试]$NC devDeviceId: $UUID"
Write-Host "$BLUE[调试]$NC sqmId: $SQM_ID"
# 显示文件树结构
Write-Host ""
Write-Host "$GREEN[信息]$NC 文件结构:"
Write-Host "$BLUE$env:APPDATA\Cursor\User$NC"
Write-Host "├── globalStorage"
Write-Host "│ ├── storage.json (已修改)"
Write-Host "│ └── backups"
# 列出备份文件
$backupFiles = Get-ChildItem "$BACKUP_DIR\*" -ErrorAction SilentlyContinue
if ($backupFiles) {
foreach ($file in $backupFiles) {
Write-Host "│ └── $($file.Name)"
}
} else {
Write-Host "│ └── (空)"
}
# 显示公众号信息
Write-Host ""
Write-Host "$GREEN================================$NC"
Write-Host "$YELLOW 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) $NC"
Write-Host "$GREEN================================$NC"
Write-Host ""
Write-Host "$GREEN[信息]$NC 请重启 Cursor 以应用新的配置"
Write-Host ""
# 询问是否要禁用自动更新
Write-Host ""
Write-Host "$YELLOW[询问]$NC 是否要禁用 Cursor 自动更新功能?"
Write-Host "0) 否 - 保持默认设置 (按回车键)"
Write-Host "1) 是 - 禁用自动更新"
$choice = Read-Host "请输入选项 (0)"
if ($choice -eq "1") {
Write-Host ""
Write-Host "$GREEN[信息]$NC 正在处理自动更新..."
$updaterPath = "$env:LOCALAPPDATA\cursor-updater"
# 定义手动设置教程
function Show-ManualGuide {
Write-Host ""
Write-Host "$YELLOW[警告]$NC 自动设置失败,请尝试手动操作:"
Write-Host "$YELLOW手动禁用更新步骤$NC"
Write-Host "1. 以管理员身份打开 PowerShell"
Write-Host "2. 复制粘贴以下命令:"
Write-Host "$BLUE命令1 - 删除现有目录(如果存在):$NC"
Write-Host "Remove-Item -Path `"$updaterPath`" -Force -Recurse -ErrorAction SilentlyContinue"
Write-Host ""
Write-Host "$BLUE命令2 - 创建阻止文件:$NC"
Write-Host "New-Item -Path `"$updaterPath`" -ItemType File -Force | Out-Null"
Write-Host ""
Write-Host "$BLUE命令3 - 设置只读属性:$NC"
Write-Host "Set-ItemProperty -Path `"$updaterPath`" -Name IsReadOnly -Value `$true"
Write-Host ""
Write-Host "$BLUE命令4 - 设置权限(可选):$NC"
Write-Host "icacls `"$updaterPath`" /inheritance:r /grant:r `"`$($env:USERNAME):(R)`""
Write-Host ""
Write-Host "$YELLOW验证方法$NC"
Write-Host "1. 运行命令Get-ItemProperty `"$updaterPath`""
Write-Host "2. 确认 IsReadOnly 属性为 True"
Write-Host "3. 运行命令icacls `"$updaterPath`""
Write-Host "4. 确认只有读取权限"
Write-Host ""
Write-Host "$YELLOW[提示]$NC 完成后请重启 Cursor"
}
try {
# 删除现有目录
if (Test-Path $updaterPath) {
try {
Remove-Item -Path $updaterPath -Force -Recurse -ErrorAction Stop
Write-Host "$GREEN[信息]$NC 成功删除 cursor-updater 目录"
}
catch {
Write-Host "$RED[错误]$NC 删除 cursor-updater 目录失败"
Show-ManualGuide
return
}
}
# 创建阻止文件
try {
New-Item -Path $updaterPath -ItemType File -Force -ErrorAction Stop | Out-Null
Write-Host "$GREEN[信息]$NC 成功创建阻止文件"
}
catch {
Write-Host "$RED[错误]$NC 创建阻止文件失败"
Show-ManualGuide
return
}
# 设置文件权限
try {
# 设置只读属性
Set-ItemProperty -Path $updaterPath -Name IsReadOnly -Value $true -ErrorAction Stop
# 使用 icacls 设置权限
$result = Start-Process "icacls.exe" -ArgumentList "`"$updaterPath`" /inheritance:r /grant:r `"$($env:USERNAME):(R)`"" -Wait -NoNewWindow -PassThru
if ($result.ExitCode -ne 0) {
throw "icacls 命令失败"
}
Write-Host "$GREEN[信息]$NC 成功设置文件权限"
}
catch {
Write-Host "$RED[错误]$NC 设置文件权限失败"
Show-ManualGuide
return
}
# 验证设置
try {
$fileInfo = Get-ItemProperty $updaterPath
if (-not $fileInfo.IsReadOnly) {
Write-Host "$RED[错误]$NC 验证失败:文件权限设置可能未生效"
Show-ManualGuide
return
}
}
catch {
Write-Host "$RED[错误]$NC 验证设置失败"
Show-ManualGuide
return
}
Write-Host "$GREEN[信息]$NC 成功禁用自动更新"
}
catch {
Write-Host "$RED[错误]$NC 发生未知错误: $_"
Show-ManualGuide
}
}
else {
Write-Host "$GREEN[信息]$NC 保持默认设置,不进行更改"
}
} catch {
Write-Host "$RED[错误]$NC 主要操作失败: $_"
Write-Host "$YELLOW[尝试]$NC 使用备选方法..."
try {
# 备选方法:使用 Add-Content
$tempFile = [System.IO.Path]::GetTempFileName()
$config | ConvertTo-Json | Set-Content -Path $tempFile -Encoding UTF8
Copy-Item -Path $tempFile -Destination $STORAGE_FILE -Force
Remove-Item -Path $tempFile
Write-Host "$GREEN[信息]$NC 使用备选方法成功写入配置"
} catch {
Write-Host "$RED[错误]$NC 所有尝试都失败了"
Write-Host "错误详情: $_"
Write-Host "目标文件: $STORAGE_FILE"
Write-Host "请确保您有足够的权限访问该文件"
Read-Host "按回车键退出"
exit 1
}
}
Write-Host ""
Read-Host "按回车键退出"
exit 0
# 在文件写入部分修改
function Write-ConfigFile {
param($config, $filePath)
try {
# 使用 UTF8 无 BOM 编码
$utf8NoBom = New-Object System.Text.UTF8Encoding $false
$jsonContent = $config | ConvertTo-Json -Depth 10
# 统一使用 LF 换行符
$jsonContent = $jsonContent.Replace("`r`n", "`n")
[System.IO.File]::WriteAllText(
[System.IO.Path]::GetFullPath($filePath),
$jsonContent,
$utf8NoBom
)
Write-Host "$GREEN[信息]$NC 成功写入配置文件(UTF8 无 BOM)"
}
catch {
throw "写入配置文件失败: $_"
}
}
function Compare-Version {
param (
[string]$version1,
[string]$version2
)
try {
$v1 = [version]($version1 -replace '[^\d\.].*$')
$v2 = [version]($version2 -replace '[^\d\.].*$')
return $v1.CompareTo($v2)
}
catch {
Write-Host "$RED[错误]$NC 版本比较失败: $_"
return 0
}
}
# 在主流程开始时添加版本检查
Write-Host "$GREEN[信息]$NC 正在检查 Cursor 版本..."
$cursorVersion = Get-CursorVersion
if ($cursorVersion) {
$compareResult = Compare-Version $cursorVersion "0.45.0"
if ($compareResult -ge 0) {
Write-Host "$RED[错误]$NC 当前版本 ($cursorVersion) 暂不支持"
Write-Host "$YELLOW[建议]$NC 请使用 v0.44.11 及以下版本"
Write-Host "$YELLOW[建议]$NC 可以从以下地址下载支持的版本:"
Write-Host "Windows: https://download.todesktop.com/230313mzl4w4u92/Cursor%20Setup%200.44.11%20-%20Build%20250103fqxdt5u9z-x64.exe"
Write-Host "Mac ARM64: https://dl.todesktop.com/230313mzl4w4u92/versions/0.44.11/mac/zip/arm64"
Read-Host "按回车键退出"
exit 1
}
else {
Write-Host "$GREEN[信息]$NC 当前版本 ($cursorVersion) 支持重置功能"
}
}
else {
Write-Host "$YELLOW[警告]$NC 无法检测版本,将继续执行..."
}

View File

@@ -0,0 +1,59 @@
{
"package_json_changes": {},
"registry_changes": {
"HKLM_MachineGuid": {
"before": {
"exists": true,
"value": "d890ab3d-43cd-40c8-a9ef-f5683b5a64e3"
},
"after": {
"exists": true,
"value": "776c6b6c-195f-42dc-94d6-72b70c3aca74"
}
}
},
"file_changes": {
"storage": {
"before": {
"exists": true,
"is_dir": false,
"size": 3427,
"modified_time": "2025-02-12T11:40:57.046415"
},
"after": {
"exists": true,
"is_dir": false,
"size": 1891,
"modified_time": "2025-02-12T11:48:42.627574"
}
},
"storage_backup": {
"before": {
"exists": true,
"is_dir": true,
"size": null,
"modified_time": "2025-02-12T11:00:01.555876"
},
"after": {
"exists": true,
"is_dir": true,
"size": null,
"modified_time": "2025-02-12T11:48:26.403770"
}
},
"global_storage": {
"before": {
"exists": true,
"is_dir": true,
"size": null,
"modified_time": "2025-02-12T11:47:27.627883"
},
"after": {
"exists": true,
"is_dir": true,
"size": null,
"modified_time": "2025-02-12T11:48:47.490659"
}
}
}
}

View File

@@ -1,11 +1,14 @@
import sys import sys
import tkinter as tk
from tkinter import ttk, messagebox
from pathlib import Path from pathlib import Path
import logging import logging
import os import os
from PIL import Image, ImageTk from PIL import Image
import time from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QLineEdit, QPushButton, QFrame, QTextEdit,
QMessageBox, QApplication)
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QIcon, QPixmap
sys.path.append(str(Path(__file__).parent.parent)) sys.path.append(str(Path(__file__).parent.parent))
from utils.config import Config from utils.config import Config
@@ -20,149 +23,120 @@ def get_version():
logging.error(f"读取版本号失败: {str(e)}") logging.error(f"读取版本号失败: {str(e)}")
return "未知版本" return "未知版本"
class MainWindow: class MainWindow(QMainWindow):
def __init__(self): def __init__(self):
super().__init__()
self.config = Config() self.config = Config()
self.switcher = AccountSwitcher() self.switcher = AccountSwitcher()
self.root = tk.Tk()
version = get_version() version = get_version()
cursor_version = self.switcher.get_cursor_version() cursor_version = self.switcher.get_cursor_version()
self.root.title(f"听泉Cursor助手 v{version} (本机Cursor版本: {cursor_version})") self.setWindowTitle(f"听泉Cursor助手 v{version} (本机Cursor版本: {cursor_version})")
self.root.geometry("600x500") # 调整窗口大小 self.setMinimumSize(600, 500)
self.root.resizable(True, True) # 允许调整窗口大小
# 设置窗口最小尺寸
self.root.minsize(600, 500)
# 设置图标
try: try:
# 设置图标 - 使用PIL
current_dir = os.path.dirname(os.path.abspath(__file__)) current_dir = os.path.dirname(os.path.abspath(__file__))
icon_path = os.path.join(os.path.dirname(current_dir), "icon", "th.jpg") icon_path = os.path.join(os.path.dirname(current_dir), "icon", "th.jpg")
if os.path.exists(icon_path): if os.path.exists(icon_path):
# 打开并调整图片大小 self.setWindowIcon(QIcon(icon_path))
img = Image.open(icon_path)
img = img.resize((32, 32), Image.Resampling.LANCZOS)
# 转换为PhotoImage
self.icon = ImageTk.PhotoImage(img)
self.root.iconphoto(True, self.icon)
logging.info(f"成功加载图标: {icon_path}") logging.info(f"成功加载图标: {icon_path}")
else: else:
logging.error(f"图标文件不存在: {icon_path}") logging.error(f"图标文件不存在: {icon_path}")
except Exception as e: except Exception as e:
logging.error(f"设置图标失败: {str(e)}") logging.error(f"设置图标失败: {str(e)}")
# 设置关闭窗口处理 # 创建主窗口部件
self.root.protocol("WM_DELETE_WINDOW", self.on_closing) central_widget = QWidget()
self.setCentralWidget(central_widget)
# 初始化状态变量 # 创建主布局
self.status_var = tk.StringVar(value="") main_layout = QVBoxLayout(central_widget)
# 设置样式
self.style = ttk.Style()
self.style.configure("TButton", padding=5, font=("Microsoft YaHei UI", 9))
self.style.configure("TLabelframe", padding=10, font=("Microsoft YaHei UI", 9))
self.style.configure("TLabel", padding=2, font=("Microsoft YaHei UI", 9))
self.style.configure("Custom.TButton", padding=10, font=("Microsoft YaHei UI", 9))
self.style.configure("Action.TButton", padding=8, font=("Microsoft YaHei UI", 9))
self.setup_ui()
# 启动时检查一次状态
self.check_status()
def setup_ui(self):
"""设置UI界面"""
# 主框架
main_frame = ttk.Frame(self.root, padding=10)
main_frame.pack(fill="both", expand=True)
# 功能菜单
menu_frame = ttk.Frame(main_frame)
menu_frame.pack(fill="x", pady=(0, 10))
ttk.Label(menu_frame, text="功能(F)").pack(side="left")
# 设备ID区域 # 设备ID区域
device_frame = ttk.Frame(main_frame) device_frame = QFrame()
device_frame.pack(fill="x", pady=(0, 10)) device_layout = QHBoxLayout(device_frame)
device_layout.addWidget(QLabel("设备识别码(勿动):"))
ttk.Label(device_frame, text="设备识别码(勿动):").pack(side="left") self.hardware_id_edit = QLineEdit(self.switcher.hardware_id)
self.hardware_id_var = tk.StringVar(value=self.switcher.hardware_id) self.hardware_id_edit.setReadOnly(True)
device_id_entry = ttk.Entry(device_frame, textvariable=self.hardware_id_var, width=35, state="readonly") device_layout.addWidget(self.hardware_id_edit)
device_id_entry.pack(side="left", padx=5) copy_btn = QPushButton("复制ID")
copy_btn.clicked.connect(self.copy_device_id)
copy_btn = ttk.Button(device_frame, text="复制ID", command=self.copy_device_id, width=8) device_layout.addWidget(copy_btn)
copy_btn.pack(side="left") main_layout.addWidget(device_frame)
# 会员状态区域 # 会员状态区域
status_frame = ttk.LabelFrame(main_frame, text="会员状态") status_frame = QFrame()
status_frame.pack(fill="x", pady=(0, 10)) status_layout = QVBoxLayout(status_frame)
status_layout.addWidget(QLabel("会员状态"))
self.status_text = tk.Text(status_frame, height=5, width=40, font=("Microsoft YaHei UI", 9)) self.status_text = QTextEdit()
self.status_text.pack(fill="both", padx=5, pady=5) self.status_text.setReadOnly(True)
self.status_text.config(state="disabled") self.status_text.setMinimumHeight(100)
status_layout.addWidget(self.status_text)
main_layout.addWidget(status_frame)
# 激活区域 # 激活区域
activation_frame = ttk.LabelFrame(main_frame, text="激活(叠加)会员,多个激活码可叠加整体时长") activation_frame = QFrame()
activation_frame.pack(fill="x", pady=(0, 10)) activation_layout = QVBoxLayout(activation_frame)
activation_layout.addWidget(QLabel("激活(叠加)会员,多个激活码可叠加整体时长"))
input_frame = ttk.Frame(activation_frame) input_frame = QFrame()
input_frame.pack(fill="x", padx=5, pady=5) input_layout = QHBoxLayout(input_frame)
input_layout.addWidget(QLabel("激活码:"))
ttk.Label(input_frame, text="激活码:").pack(side="left") self.activation_edit = QLineEdit()
self.activation_var = tk.StringVar() input_layout.addWidget(self.activation_edit)
activation_entry = ttk.Entry(input_frame, textvariable=self.activation_var, width=35) activate_btn = QPushButton("激活")
activation_entry.pack(side="left", padx=5) activate_btn.clicked.connect(self.activate_account)
input_layout.addWidget(activate_btn)
activate_btn = ttk.Button(input_frame, text="激活", command=self.activate_account, width=8) activation_layout.addWidget(input_frame)
activate_btn.pack(side="left") main_layout.addWidget(activation_frame)
# 操作按钮区域 # 操作按钮区域
btn_frame = ttk.Frame(main_frame) btn_frame = QFrame()
btn_frame.pack(fill="x", pady=5) btn_layout = QVBoxLayout(btn_frame)
refresh_btn = QPushButton("刷新Cursor编辑器授权")
refresh_btn.clicked.connect(self.refresh_cursor_auth)
btn_layout.addWidget(refresh_btn)
bypass_btn = QPushButton("突破Cursor0.45.x限制")
bypass_btn.clicked.connect(self.dummy_function)
btn_layout.addWidget(bypass_btn)
disable_update_btn = QPushButton("禁用Cursor版本更新")
disable_update_btn.clicked.connect(self.disable_cursor_update)
btn_layout.addWidget(disable_update_btn)
main_layout.addWidget(btn_frame)
self.style.configure("Action.TButton", padding=8) # 启动时检查一次状态
ttk.Button(btn_frame, text="刷新Cursor编辑器授权", command=self.reset_machine_id, style="Action.TButton").pack(fill="x", pady=2) QTimer.singleShot(0, self.check_status)
ttk.Button(btn_frame, text="突破Cursor0.45.x限制", command=self.dummy_function, style="Action.TButton").pack(fill="x", pady=2)
ttk.Button(btn_frame, text="禁用Cursor版本更新", command=self.disable_cursor_update, style="Action.TButton").pack(fill="x", pady=2)
def copy_device_id(self): def copy_device_id(self):
"""复制设备ID到剪贴板""" """复制设备ID到剪贴板"""
# 先检查状态
if not self.check_status(): if not self.check_status():
return return
self.root.clipboard_clear() QApplication.clipboard().setText(self.hardware_id_edit.text())
self.root.clipboard_append(self.hardware_id_var.get()) QMessageBox.information(self, "提示", "设备ID已复制到剪贴板")
def activate_account(self): def activate_account(self):
"""激活账号""" """激活账号"""
code = self.activation_var.get().strip() code = self.activation_edit.text().strip()
if not code: if not code:
messagebox.showwarning("提示", "请输入激活码") QMessageBox.warning(self, "提示", "请输入激活码")
return return
self.status_var.set("正在激活...")
self.root.update()
try: try:
success, message, account_info = self.switcher.check_activation_code(code) success, message, account_info = self.switcher.check_activation_code(code)
if success: if success:
# 更新会员信息显示 # 更新会员信息显示
self.update_status_display(account_info) self.update_status_display(account_info)
messagebox.showinfo("激活成功", "激活成功!\n" + message) QMessageBox.information(self, "激活成功", "激活成功!\n" + message)
self.status_var.set("激活成功")
# 清空激活码输入框 # 清空激活码输入框
self.activation_var.set("") self.activation_edit.clear()
else: else:
messagebox.showerror("激活失败", message) QMessageBox.critical(self, "激活失败", message)
self.status_var.set("激活失败")
# 激活后检查一次状态 # 激活后检查一次状态
self.check_status() self.check_status()
except Exception as e: except Exception as e:
messagebox.showerror("错误", f"激活失败: {str(e)}") QMessageBox.critical(self, "错误", f"激活失败: {str(e)}")
self.status_var.set("发生错误")
# 出错后也检查状态 # 出错后也检查状态
self.check_status() self.check_status()
@@ -176,11 +150,6 @@ class MainWindow:
for record in status_info['activation_records']: for record in status_info['activation_records']:
logging.info(f"- 记录: {record}") logging.info(f"- 记录: {record}")
# 启用文本框编辑
self.status_text.config(state="normal")
# 清空当前内容
self.status_text.delete(1.0, tk.END)
# 更新状态文本 # 更新状态文本
status_map = { status_map = {
"active": "正常", "active": "正常",
@@ -217,25 +186,16 @@ class MainWindow:
f"地区:{device_info.get('location', '--')}" f"地区:{device_info.get('location', '--')}"
]) ])
# 写入状态信息 # 更新状态文本
self.status_text.insert(tk.END, "\n".join(status_lines)) self.status_text.setPlainText("\n".join(status_lines))
# 禁用文本框编辑
self.status_text.config(state="disabled")
def check_status(self): def check_status(self):
"""检查会员状态""" """检查会员状态"""
try: try:
self.status_var.set("正在检查状态...")
self.root.update()
status = self.switcher.get_member_status() status = self.switcher.get_member_status()
if status: if status:
self.update_status_display(status) self.update_status_display(status)
if status.get('status') == 'inactive': return status.get('status') != 'inactive'
self.status_var.set("未激活")
return False
self.status_var.set("状态检查完成")
return True
else: else:
# 更新为未激活状态 # 更新为未激活状态
inactive_status = { inactive_status = {
@@ -247,148 +207,34 @@ class MainWindow:
"activation_records": [] "activation_records": []
} }
self.update_status_display(inactive_status) self.update_status_display(inactive_status)
self.status_var.set("未激活")
return False return False
except Exception as e: except Exception as e:
logging.error(f"检查状态失败: {str(e)}") QMessageBox.critical(self, "错误", f"检查状态失败: {str(e)}")
self.status_var.set("状态检查失败")
messagebox.showerror("错误", f"检查状态失败: {str(e)}")
return False return False
def show_purchase_info(self): def refresh_cursor_auth(self):
"""显示购买信息""" """刷新Cursor授权"""
# 创建自定义对话框
dialog = tk.Toplevel(self.root)
dialog.title("提示")
dialog.geometry("400x280") # 增加高度
dialog.resizable(False, False)
# 设置模态
dialog.transient(self.root)
dialog.grab_set()
# 创建提示图标
icon_frame = ttk.Frame(dialog)
icon_frame.pack(pady=10)
ttk.Label(icon_frame, text="", font=("Segoe UI", 24)).pack()
# 创建消息文本
message_frame = ttk.Frame(dialog)
message_frame.pack(pady=5, padx=20)
ttk.Label(message_frame, text="您还不是会员,请先购买激活会员用讯。", font=("Microsoft YaHei UI", 10)).pack()
# 创建可复制的文本框
buy_info = "闲鱼xxxx\n微信behikcigar\n网站自助购买nezhacursor.nosqli.com"
text = tk.Text(dialog, height=4, width=40, font=("Microsoft YaHei UI", 9))
text.pack(pady=5, padx=20)
text.insert("1.0", buy_info)
text.config(state="normal") # 允许选择和复制
# 创建按钮框架
btn_frame = ttk.Frame(dialog)
btn_frame.pack(pady=10)
# 复制按钮
def copy_info():
dialog.clipboard_clear()
dialog.clipboard_append(buy_info)
messagebox.showinfo("提示", "购买信息已复制到剪贴板", parent=dialog)
ttk.Button(btn_frame, text="复制信息", command=copy_info).pack(side="left", padx=5)
ttk.Button(btn_frame, text="确定", command=dialog.destroy).pack(side="left", padx=5)
# 设置对话框位置为居中
dialog.update_idletasks()
width = dialog.winfo_width()
height = dialog.winfo_height()
x = (dialog.winfo_screenwidth() // 2) - (width // 2)
y = (dialog.winfo_screenheight() // 2) - (height // 2)
dialog.geometry(f"{width}x{height}+{x}+{y}")
# 等待对话框关闭
self.root.wait_window(dialog)
def minimize_window(self):
"""最小化窗口"""
self.root.iconify()
def maximize_window(self):
"""最大化/还原窗口"""
if self.root.state() == 'zoomed':
self.root.state('normal')
else:
self.root.state('zoomed')
def on_closing(self):
"""窗口关闭处理"""
try:
logging.info("正在关闭程序...")
# 先退出主循环
self.root.quit()
# 等待一小段时间确保资源释放
time.sleep(0.5)
except Exception as e:
logging.error(f"关闭程序时出错: {str(e)}")
finally:
try:
# 销毁窗口
self.root.destroy()
# 再等待一小段时间
time.sleep(0.5)
# 强制结束进程
if sys.platform == "win32":
import os
os._exit(0)
except:
# 如果还是无法正常关闭,直接强制退出
os._exit(0)
def run(self):
"""运行程序"""
self.root.mainloop()
def reset_machine_id(self):
"""刷新Cursor编辑器授权"""
if not self.check_status():
self.show_purchase_info()
return
try: try:
success, message = self.switcher.refresh_cursor_auth() success, message = self.switcher.refresh_cursor_auth()
if success: if success:
messagebox.showinfo("成功", "Cursor编辑器授权刷新成功\n" + message) QMessageBox.information(self, "成功", message)
else: else:
messagebox.showerror("错误", message) QMessageBox.critical(self, "失败", message)
except Exception as e: except Exception as e:
messagebox.showerror("错误", f"刷新失败: {str(e)}") QMessageBox.critical(self, "错误", f"刷新授权失败: {str(e)}")
def disable_cursor_update(self): def disable_cursor_update(self):
"""禁用Cursor版本更新""" """禁用Cursor更新"""
if not self.check_status():
self.show_purchase_info()
return
try: try:
success, message = self.switcher.disable_cursor_update() success, message = self.switcher.disable_cursor_update()
if success: if success:
messagebox.showinfo("成功", message) QMessageBox.information(self, "成功", message)
else: else:
messagebox.showerror("错误", message) QMessageBox.critical(self, "失败", message)
except Exception as e: except Exception as e:
messagebox.showerror("错误", f"操作失败: {str(e)}") QMessageBox.critical(self, "错误", f"禁用更新失败: {str(e)}")
def dummy_function(self): def dummy_function(self):
"""突破版本限制""" """占位函数"""
if not self.check_status(): QMessageBox.information(self, "提示", "此功能暂未实现")
self.show_purchase_info()
return
try:
success, message = self.switcher.bypass_version_limit()
if success:
messagebox.showinfo("成功", message)
else:
messagebox.showerror("错误", message)
except Exception as e:
messagebox.showerror("错误", f"操作失败: {str(e)}")

34
main.py
View File

@@ -1,9 +1,29 @@
import logging import logging
import sys import sys
import traceback import traceback
import os
import atexit
import shutil
import tempfile
from pathlib import Path from pathlib import Path
from PyQt5.QtWidgets import QApplication, QMessageBox
from gui.main_window import MainWindow from gui.main_window import MainWindow
def cleanup_temp():
"""清理临时文件"""
try:
temp_dir = Path(tempfile._get_default_tempdir())
for item in temp_dir.glob('_MEI*'):
try:
if item.is_dir():
shutil.rmtree(str(item), ignore_errors=True)
elif item.is_file():
item.unlink()
except:
pass
except:
pass
def setup_logging(): def setup_logging():
"""设置日志""" """设置日志"""
try: try:
@@ -27,6 +47,9 @@ def setup_logging():
def main(): def main():
"""主函数""" """主函数"""
try: try:
# 注册退出时的清理函数
atexit.register(cleanup_temp)
setup_logging() setup_logging()
# 检查Python版本 # 检查Python版本
@@ -41,17 +64,20 @@ def main():
logging.info(f" - {p}") logging.info(f" - {p}")
logging.info("正在初始化主窗口...") logging.info("正在初始化主窗口...")
app = QApplication(sys.argv)
window = MainWindow() window = MainWindow()
logging.info("正在启动主窗口...") logging.info("正在启动主窗口...")
window.run() window.show()
sys.exit(app.exec_())
except Exception as e: except Exception as e:
error_msg = f"程序运行出错: {str(e)}\n{traceback.format_exc()}" error_msg = f"程序运行出错: {str(e)}\n{traceback.format_exc()}"
logging.error(error_msg) logging.error(error_msg)
# 使用tkinter的消息框显示错误 # 使用 QMessageBox 显示错误
from tkinter import messagebox app = QApplication(sys.argv)
messagebox.showerror("错误", error_msg) QMessageBox.critical(None, "错误", error_msg)
sys.exit(1)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -0,0 +1,106 @@
{
"github_changes": {
"package_json_changes": {},
"registry_changes": {
"HKLM_MachineGuid": {
"before": {
"exists": true,
"value": "d890ab3d-43cd-40c8-a9ef-f5683b5a64e3"
},
"after": {
"exists": true,
"value": "776c6b6c-195f-42dc-94d6-72b70c3aca74"
}
}
},
"file_changes": {
"storage": {
"before": {
"exists": true,
"is_dir": false,
"size": 3427,
"modified_time": "2025-02-12T11:40:57.046415"
},
"after": {
"exists": true,
"is_dir": false,
"size": 1891,
"modified_time": "2025-02-12T11:48:42.627574"
}
},
"storage_backup": {
"before": {
"exists": true,
"is_dir": true,
"size": null,
"modified_time": "2025-02-12T11:00:01.555876"
},
"after": {
"exists": true,
"is_dir": true,
"size": null,
"modified_time": "2025-02-12T11:48:26.403770"
}
},
"global_storage": {
"before": {
"exists": true,
"is_dir": true,
"size": null,
"modified_time": "2025-02-12T11:47:27.627883"
},
"after": {
"exists": true,
"is_dir": true,
"size": null,
"modified_time": "2025-02-12T11:48:47.490659"
}
}
}
},
"our_changes": {
"package_json_changes": {},
"registry_changes": {
"HKLM_MachineGuid": {
"before": {
"exists": true,
"value": "948b93c1-ee34-4a48-95d0-a2fce9af92b1"
},
"after": {
"exists": true,
"value": "1deb25e7-cdd4-4367-a347-fba8b33b9b03"
}
}
},
"file_changes": {
"storage_backup": {
"before": {
"exists": true,
"is_dir": true,
"size": null,
"modified_time": "2025-02-12T12:33:10.408749"
},
"after": {
"exists": true,
"is_dir": true,
"size": null,
"modified_time": "2025-02-12T12:37:17.371311"
}
},
"storage": {
"before": {
"exists": true,
"is_dir": false,
"size": 2214,
"modified_time": "2025-02-12T12:36:27.187387"
},
"after": {
"exists": true,
"is_dir": false,
"size": 1980,
"modified_time": "2025-02-12T12:37:17.428609"
}
}
}
}
}

295
test_compare_reset.py Normal file
View File

@@ -0,0 +1,295 @@
import json
import logging
import os
import time
import subprocess
from pathlib import Path
from datetime import datetime
from account_switcher import AccountSwitcher
def get_full_machine_info():
"""获取更完整的机器码相关信息"""
info = {}
# 1. 获取 package.json 中的所有信息(检查两个可能的路径)
cursor_paths = [
Path(os.path.expanduser("~")) / "AppData" / "Local" / "Programs" / "Cursor",
Path(os.path.expanduser("~")) / "AppData" / "Local" / "cursor"
]
info["package_json"] = {}
for cursor_path in cursor_paths:
package_json = cursor_path / "resources" / "app" / "package.json"
if package_json.exists():
with open(package_json, "r", encoding="utf-8") as f:
info["package_json"][str(package_json)] = json.load(f)
# 2. 获取 storage.json 的内容
storage_path = Path(os.getenv('APPDATA')) / "Cursor" / "User" / "globalStorage" / "storage.json"
info["storage_json"] = None
if storage_path.exists():
try:
with open(storage_path, "r", encoding="utf-8") as f:
info["storage_json"] = json.load(f)
except:
pass
# 3. 检查注册表项和其值
import winreg
registry_paths = {
"cryptography": r"SOFTWARE\Microsoft\Cryptography", # 系统 MachineGuid
"cursor_shell": r"Software\Classes\Directory\Background\shell\Cursor",
"cursor_command": r"Software\Classes\Directory\Background\shell\Cursor\command",
"cursor_auth": r"Software\Cursor\Auth",
"cursor_updates": r"Software\Cursor\Updates",
"cursor_main": r"Software\Cursor"
}
info["registry"] = {}
# HKEY_LOCAL_MACHINE 项
try:
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, registry_paths["cryptography"], 0, winreg.KEY_READ | winreg.KEY_WOW64_64KEY)
info["registry"]["HKLM_MachineGuid"] = {
"exists": True,
"value": winreg.QueryValueEx(key, "MachineGuid")[0]
}
except WindowsError:
info["registry"]["HKLM_MachineGuid"] = {
"exists": False,
"value": None
}
# HKEY_CURRENT_USER 项
for name, path in registry_paths.items():
if name == "cryptography":
continue
try:
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, path, 0, winreg.KEY_READ)
info["registry"][f"HKCU_{name}"] = {"exists": True, "values": {}}
try:
i = 0
while True:
name, value, type = winreg.EnumValue(key, i)
info["registry"][f"HKCU_{name}"]["values"][name] = value
i += 1
except WindowsError:
pass
except WindowsError:
info["registry"][f"HKCU_{name}"] = {"exists": False, "values": {}}
# 4. 检查关键文件的存在状态
paths_to_check = {
"storage": storage_path,
"storage_backup": Path(os.getenv('APPDATA')) / "Cursor" / "User" / "globalStorage" / "backups",
"user_data": Path(os.getenv('LOCALAPPDATA')) / "Cursor" / "User",
"global_storage": Path(os.getenv('APPDATA')) / "Cursor" / "User" / "globalStorage",
"cache": Path(os.getenv('LOCALAPPDATA')) / "Cursor" / "Cache",
"updater": Path(os.getenv('LOCALAPPDATA')) / "cursor-updater"
}
info["files"] = {}
for name, path in paths_to_check.items():
if path.exists():
info["files"][name] = {
"exists": True,
"is_dir": path.is_dir(),
"size": os.path.getsize(path) if path.is_file() else None,
"modified_time": datetime.fromtimestamp(os.path.getmtime(path)).isoformat()
}
else:
info["files"][name] = {
"exists": False,
"is_dir": None,
"size": None,
"modified_time": None
}
# 5. 获取遥测相关的配置值
info["telemetry"] = {
"machineId": info["storage_json"].get("telemetry.machineId") if info["storage_json"] else None,
"macMachineId": info["storage_json"].get("telemetry.macMachineId") if info["storage_json"] else None,
"devDeviceId": info["storage_json"].get("telemetry.devDeviceId") if info["storage_json"] else None,
"sqmId": info["storage_json"].get("telemetry.sqmId") if info["storage_json"] else None
}
return info
def save_info(info, filename):
"""保存信息到文件"""
with open(filename, "w", encoding="utf-8") as f:
json.dump(info, f, indent=2, ensure_ascii=False)
def compare_info(before_file, after_file):
"""比较两个信息文件的差异"""
with open(before_file, "r", encoding="utf-8") as f:
before = json.load(f)
with open(after_file, "r", encoding="utf-8") as f:
after = json.load(f)
differences = {
"package_json_changes": {},
"registry_changes": {},
"file_changes": {}
}
# 比较 package.json 变化
if "package_json" in before and "package_json" in after:
for key in set(before["package_json"].keys()) | set(after["package_json"].keys()):
before_val = before["package_json"].get(key)
after_val = after["package_json"].get(key)
if before_val != after_val:
differences["package_json_changes"][key] = {
"before": before_val,
"after": after_val
}
# 比较注册表变化
for path in set(before["registry"].keys()) | set(after["registry"].keys()):
before_reg = before["registry"].get(path, {})
after_reg = after["registry"].get(path, {})
if before_reg != after_reg:
differences["registry_changes"][path] = {
"before": before_reg,
"after": after_reg
}
# 比较文件变化
for name in set(before["files"].keys()) | set(after["files"].keys()):
before_file = before["files"].get(name, {})
after_file = after["files"].get(name, {})
if before_file != after_file:
differences["file_changes"][name] = {
"before": before_file,
"after": after_file
}
return differences
def main():
"""主测试流程"""
# 设置日志
logging.basicConfig(level=logging.INFO)
print("选择操作:")
print("1. 记录当前状态(在运行 GitHub 脚本前执行)")
print("2. 记录重置后状态并对比(在运行 GitHub 脚本后执行)")
print("3. 运行我们的重置方法并记录对比")
choice = input("\n请选择操作 (1/2/3): ").strip()
if choice == "1":
# 记录初始状态
print("\n记录初始状态...")
initial_info = get_full_machine_info()
save_info(initial_info, "before_github_reset.json")
print("初始状态已保存到 before_github_reset.json")
elif choice == "2":
# 检查是否存在初始状态文件
if not os.path.exists("before_github_reset.json"):
print("错误:找不到初始状态文件 before_github_reset.json")
print("请先执行选项 1 记录初始状态")
return
# 记录 GitHub 脚本执行后的状态
print("\n记录 GitHub 脚本执行后的状态...")
after_info = get_full_machine_info()
save_info(after_info, "after_github_reset.json")
# 对比变化
print("\n=== GitHub 脚本执行前后的变化 ===")
differences = compare_info("before_github_reset.json", "after_github_reset.json")
# 显示 package.json 的变化
if differences["package_json_changes"]:
print("\npackage.json 变化:")
for key, change in differences["package_json_changes"].items():
print(f"- {key}:")
print(f" 之前: {change['before']}")
print(f" 之后: {change['after']}")
# 显示注册表的变化
if differences["registry_changes"]:
print("\n注册表变化:")
for path, change in differences["registry_changes"].items():
print(f"\n- {path}:")
print(f" 之前: {change['before']}")
print(f" 之后: {change['after']}")
# 显示文件的变化
if differences["file_changes"]:
print("\n文件变化:")
for name, change in differences["file_changes"].items():
print(f"\n- {name}:")
before_status = "存在" if change["before"].get("exists") else "不存在"
after_status = "存在" if change["after"].get("exists") else "不存在"
print(f" 状态: {before_status} -> {after_status}")
if change["before"].get("exists") and change["after"].get("exists"):
if change["before"].get("size") != change["after"].get("size"):
print(f" 大小: {change['before'].get('size')} -> {change['after'].get('size')}")
if change["before"].get("modified_time") != change["after"].get("modified_time"):
print(f" 修改时间: {change['before'].get('modified_time')} -> {change['after'].get('modified_time')}")
# 保存差异到文件
save_info(differences, "github_script_changes.json")
print("\n详细的变化信息已保存到 github_script_changes.json")
elif choice == "3":
if not os.path.exists("github_script_changes.json"):
print("错误:找不到 GitHub 脚本的变化记录文件 github_script_changes.json")
print("请先执行选项 1 和 2 来记录 GitHub 脚本的变化")
return
# 记录运行我们的重置方法前的状态
print("\n记录当前状态...")
before_our_reset = get_full_machine_info()
save_info(before_our_reset, "before_our_reset.json")
# 运行我们的重置方法
print("\n运行我们的重置方法...")
switcher = AccountSwitcher()
original_restart = switcher.restart_cursor
try:
switcher.restart_cursor = lambda: True
switcher.reset_machine_id()
finally:
switcher.restart_cursor = original_restart
# 记录重置后的状态
print("\n记录重置后的状态...")
after_our_reset = get_full_machine_info()
save_info(after_our_reset, "after_our_reset.json")
# 对比我们的方法与 GitHub 脚本的差异
print("\n=== 对比我们的重置方法与 GitHub 脚本的差异 ===")
with open("github_script_changes.json", "r", encoding="utf-8") as f:
github_changes = json.load(f)
our_changes = compare_info("before_our_reset.json", "after_our_reset.json")
# 分析差异
print("\n=== 差异分析 ===")
print("1. GitHub 脚本改变但我们没有改变的项:")
for category in ["package_json_changes", "registry_changes", "file_changes"]:
for key in github_changes[category]:
if key not in our_changes[category]:
print(f"- {category}: {key}")
print("\n2. 我们改变但 GitHub 脚本没有改变的项:")
for category in ["package_json_changes", "registry_changes", "file_changes"]:
for key in our_changes[category]:
if key not in github_changes[category]:
print(f"- {category}: {key}")
# 保存分析结果
save_info({
"github_changes": github_changes,
"our_changes": our_changes
}, "reset_methods_comparison.json")
print("\n详细的对比结果已保存到 reset_methods_comparison.json")
else:
print("无效的选择")
if __name__ == "__main__":
main()

104
test_reset.py Normal file
View File

@@ -0,0 +1,104 @@
import json
import logging
from pathlib import Path
from account_switcher import AccountSwitcher
import os
def get_machine_info():
"""获取当前机器码相关信息"""
info = {}
# 1. 获取 package.json 中的 machineId
cursor_path = Path(os.path.expanduser("~")) / "AppData" / "Local" / "Programs" / "Cursor"
package_json = cursor_path / "resources" / "app" / "package.json"
if package_json.exists():
with open(package_json, "r", encoding="utf-8") as f:
data = json.load(f)
info["machine_id"] = data.get("machineId", "未找到")
# 2. 检查注册表项是否存在
import winreg
registry_paths = [
r"Software\Classes\Directory\Background\shell\Cursor\command",
r"Software\Classes\Directory\Background\shell\Cursor",
r"Software\Cursor\Auth",
r"Software\Cursor\Updates",
r"Software\Cursor"
]
info["registry"] = {}
for path in registry_paths:
try:
winreg.OpenKey(winreg.HKEY_CURRENT_USER, path, 0, winreg.KEY_READ)
info["registry"][path] = "存在"
except WindowsError:
info["registry"][path] = "不存在"
# 3. 检查文件是否存在
local_app_data = Path(os.getenv('LOCALAPPDATA'))
files_to_check = [
local_app_data / "Cursor" / "User" / "globalStorage",
local_app_data / "Cursor" / "Cache",
local_app_data / "cursor-updater",
cursor_path / "resources" / "app" / "config.json",
cursor_path / "resources" / "app" / "state.json",
cursor_path / "resources" / "app" / "settings.json"
]
info["files"] = {}
for path in files_to_check:
info["files"][str(path)] = "存在" if path.exists() else "不存在"
return info
def test_reset_machine_id():
"""测试重置机器码功能"""
# 设置日志
logging.basicConfig(level=logging.INFO)
print("=== 重置前的状态 ===")
before_info = get_machine_info()
print(json.dumps(before_info, indent=2, ensure_ascii=False))
# 创建 AccountSwitcher 实例并修改 reset_machine_id 方法以跳过重启
switcher = AccountSwitcher()
# 保存原始的 restart_cursor 方法
original_restart = switcher.restart_cursor
try:
# 替换 restart_cursor 方法为空函数
switcher.restart_cursor = lambda: True
# 执行重置
print("\n=== 执行重置 ===")
result = switcher.reset_machine_id()
print(f"重置结果: {result}")
print("\n=== 重置后的状态 ===")
after_info = get_machine_info()
print(json.dumps(after_info, indent=2, ensure_ascii=False))
# 比较差异
print("\n=== 变化分析 ===")
# 检查 machineId
if before_info.get("machine_id") != after_info.get("machine_id"):
print(f"machineId 变化: {before_info.get('machine_id')} -> {after_info.get('machine_id')}")
# 检查注册表变化
for path in before_info["registry"]:
if before_info["registry"][path] != after_info["registry"][path]:
print(f"注册表 {path}: {before_info['registry'][path]} -> {after_info['registry'][path]}")
# 检查文件变化
for path in before_info["files"]:
if before_info["files"][path] != after_info["files"][path]:
print(f"文件 {path}: {before_info['files'][path]} -> {after_info['files'][path]}")
finally:
# 恢复原始的 restart_cursor 方法
switcher.restart_cursor = original_restart
if __name__ == "__main__":
test_reset_machine_id()

View File

@@ -1,8 +1,12 @@
import os import os
import winreg import winreg
import logging import logging
import shutil
from pathlib import Path from pathlib import Path
import uuid import uuid
from datetime import datetime
import json
import hashlib
class CursorRegistry: class CursorRegistry:
"""Cursor注册表操作工具类""" """Cursor注册表操作工具类"""
@@ -10,53 +14,61 @@ class CursorRegistry:
def __init__(self): def __init__(self):
self.cursor_path = Path(os.path.expanduser("~")) / "AppData" / "Local" / "Programs" / "Cursor" self.cursor_path = Path(os.path.expanduser("~")) / "AppData" / "Local" / "Programs" / "Cursor"
self.app_path = self.cursor_path / "resources" / "app" self.app_path = self.cursor_path / "resources" / "app"
self.machine_guid_path = r"SOFTWARE\Microsoft\Cryptography"
self.machine_guid_name = "MachineGuid"
def refresh_registry(self) -> bool: def update_machine_guid(self) -> bool:
"""刷新注册表 """更新系统的 MachineGuid
Returns: Returns:
bool: 是否成功 bool: 是否成功
""" """
try: try:
# 生成新的 GUID # 生成新的 GUID
new_guid = str(uuid.uuid4()) new_guid = str(uuid.uuid4())
registry_path = r"SOFTWARE\Microsoft\Cryptography"
# 修改 MachineGuid
try: try:
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, self.machine_guid_path, 0, winreg.KEY_ALL_ACCESS) as key: # 使用管理员权限打开注册表项
winreg.SetValueEx(key, self.machine_guid_name, 0, winreg.REG_SZ, new_guid) key = None
logging.info(f"更新 MachineGuid 成功: {new_guid}")
except Exception as e:
logging.error(f"更新 MachineGuid 失败: {str(e)}")
return False
# 获取Cursor安装路径
cursor_exe = self.cursor_path / "Cursor.exe"
if not cursor_exe.exists():
logging.error("未找到Cursor.exe")
return False
# 刷新注册表项
try: try:
# 打开HKEY_CURRENT_USER\Software\Classes\Directory\Background\shell\Cursor # 先尝试直接打开读取权限
key_path = r"Software\Classes\Directory\Background\shell\Cursor" key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, registry_path, 0,
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path, 0, winreg.KEY_WRITE) as key: winreg.KEY_READ | winreg.KEY_WOW64_64KEY)
winreg.SetValueEx(key, "Icon", 0, winreg.REG_SZ, str(cursor_exe)) # 读取原始值并备份
original_guid = winreg.QueryValueEx(key, "MachineGuid")[0]
winreg.CloseKey(key)
# 打开command子键 # 备份原始 MachineGuid
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path + r"\command", 0, winreg.KEY_WRITE) as key: backup_dir = Path(os.getenv('APPDATA')) / "Cursor" / "User" / "globalStorage" / "backups"
winreg.SetValueEx(key, "", 0, winreg.REG_SZ, f'"{str(cursor_exe)}" "%V"') backup_dir.mkdir(parents=True, exist_ok=True)
backup_name = f"MachineGuid.backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
with open(backup_dir / backup_name, 'w', encoding='utf-8') as f:
f.write(original_guid)
logging.info(f"备份 MachineGuid 到: {backup_name}")
logging.info("注册表刷新成功") # 重新打开写入权限
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, registry_path, 0,
winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY)
except WindowsError:
# 如果失败,尝试以管理员权限运行
import ctypes
if ctypes.windll.shell32.IsUserAnAdmin() == 0:
logging.warning("需要管理员权限来修改 MachineGuid")
return False
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, registry_path, 0,
winreg.KEY_ALL_ACCESS | winreg.KEY_WOW64_64KEY)
# 设置新的 GUID
winreg.SetValueEx(key, "MachineGuid", 0, winreg.REG_SZ, new_guid)
winreg.CloseKey(key)
logging.info(f"更新系统 MachineGuid 成功: {new_guid}")
return True return True
except WindowsError as e: except WindowsError as e:
logging.error(f"刷新注册表失败: {str(e)}") logging.error(f"更新系统 MachineGuid 失败: {str(e)}")
return False return False
except Exception as e: except Exception as e:
logging.error(f"刷新注册表失败: {str(e)}") logging.error(f"更新 MachineGuid 过程出错: {str(e)}")
return False return False
def clean_registry(self) -> bool: def clean_registry(self) -> bool:
@@ -66,21 +78,149 @@ class CursorRegistry:
bool: 是否成功 bool: 是否成功
""" """
try: try:
# 删除注册表项 # 需要清理的注册表路径列表
try: registry_paths = [
key_path = r"Software\Classes\Directory\Background\shell\Cursor" r"Software\Classes\Directory\Background\shell\Cursor\command",
winreg.DeleteKey(winreg.HKEY_CURRENT_USER, key_path + r"\command") r"Software\Classes\Directory\Background\shell\Cursor",
winreg.DeleteKey(winreg.HKEY_CURRENT_USER, key_path) r"Software\Cursor\Auth",
logging.info("注册表清理成功") r"Software\Cursor\Updates",
return True r"Software\Cursor"
]
for path in registry_paths:
try:
winreg.DeleteKey(winreg.HKEY_CURRENT_USER, path)
logging.info(f"删除注册表项成功: {path}")
except WindowsError as e: except WindowsError as e:
if e.winerror == 2: # 找不到注册表项 if e.winerror == 2: # 找不到注册表项
logging.info("注册表项不存在,无需清理") logging.info(f"注册表项不存在,无需清理: {path}")
else:
logging.error(f"清理注册表失败: {path}, 错误: {str(e)}")
# 更新系统 MachineGuid
self.update_machine_guid()
return True return True
logging.error(f"清理注册表失败: {str(e)}")
return False
except Exception as e: except Exception as e:
logging.error(f"清理注册表过程出错: {str(e)}") logging.error(f"清理注册表过程出错: {str(e)}")
return False return False
def clean_cursor_files(self) -> bool:
"""清理Cursor相关的文件和目录但保留重要的配置和历史记录"""
try:
local_app_data = Path(os.getenv('LOCALAPPDATA'))
app_data = Path(os.getenv('APPDATA'))
# 需要备份的文件
storage_path = app_data / "Cursor" / "User" / "globalStorage" / "storage.json"
backup_dir = app_data / "Cursor" / "User" / "globalStorage" / "backups"
global_storage_dir = app_data / "Cursor" / "User" / "globalStorage"
# 如果存在 storage.json先备份
if storage_path.exists():
# 确保备份目录存在
backup_dir.mkdir(parents=True, exist_ok=True)
# 备份 storage.json
backup_name = f"storage.json.backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
shutil.copy2(storage_path, backup_dir / backup_name)
logging.info(f"备份 storage.json 到: {backup_name}")
# 备份 global_storage 目录中的其他重要文件
if global_storage_dir.exists():
for item in global_storage_dir.iterdir():
if item.name != "storage.json" and item.name != "backups":
try:
backup_item_dir = backup_dir / f"other_files_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
backup_item_dir.mkdir(exist_ok=True)
if item.is_file():
shutil.copy2(item, backup_item_dir / item.name)
logging.info(f"备份文件: {item.name}")
elif item.is_dir():
shutil.copytree(item, backup_item_dir / item.name)
logging.info(f"备份目录: {item.name}")
except Exception as e:
logging.error(f"备份 {item} 失败: {str(e)}")
# 读取当前内容
with open(storage_path, "r", encoding="utf-8") as f:
storage_data = json.load(f)
# 只修改 machineId保持其他配置不变
if "telemetry.machineId" in storage_data:
# 生成新的 machineId
new_machine_id = hashlib.sha256(str(uuid.uuid4()).encode()).hexdigest()
storage_data["telemetry.machineId"] = new_machine_id
logging.info(f"更新 machineId: {new_machine_id}")
# 保存修改后的内容
with open(storage_path, "w", encoding="utf-8") as f:
json.dump(storage_data, f, indent=2)
# 处理 updater 目录
updater_path = local_app_data / "cursor-updater"
try:
# 如果是目录,则删除
if updater_path.is_dir():
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 空文件成功")
except Exception as e:
logging.error(f"处理 updater 文件失败: {str(e)}")
# 只清理缓存相关的路径
paths_to_clean = [
local_app_data / "Cursor" / "Cache"
]
for path in paths_to_clean:
try:
if path.is_dir():
shutil.rmtree(str(path), ignore_errors=True)
logging.info(f"删除目录成功: {path}")
elif path.exists():
path.unlink()
logging.info(f"删除文件成功: {path}")
except Exception as e:
logging.error(f"清理文件/目录失败: {path}, 错误: {str(e)}")
# 修复 Cursor 启动配置
self.fix_cursor_startup()
return True
except Exception as e:
logging.error(f"清理文件过程出错: {str(e)}")
return False
def fix_cursor_startup(self) -> bool:
"""修复 Cursor 启动警告"""
try:
# 1. 修改 package.json 中的更新相关配置
if self.app_path.exists():
package_json = self.app_path / "package.json"
if package_json.exists():
with open(package_json, "r", encoding="utf-8") as f:
data = json.load(f)
# 只修改更新相关配置,与 GitHub 脚本保持一致
data["updateUrl"] = "" # 清空更新 URL
data["disableUpdate"] = True # 禁用更新
with open(package_json, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2)
logging.info("已修复 Cursor 启动配置")
return True
except Exception as e:
logging.error(f"修复 Cursor 启动配置失败: {str(e)}")
return False

View File

@@ -1 +1 @@
3.0.9 3.2.8