diff --git a/config.py b/config.py index cf481c0..9fb75ce 100644 --- a/config.py +++ b/config.py @@ -66,7 +66,8 @@ def setup_config(translator=None): 'machine_id_path': os.path.join(appdata, "Cursor", "machineId"), 'cursor_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app"), 'updater_path': os.path.join(localappdata, "cursor-updater"), - 'update_yml_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app-update.yml") + 'update_yml_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app-update.yml"), + 'product_json_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app", "product.json") } # Create storage directory os.makedirs(os.path.dirname(default_config['WindowsPaths']['storage_path']), exist_ok=True) @@ -78,7 +79,8 @@ def setup_config(translator=None): 'machine_id_path': os.path.expanduser("~/Library/Application Support/Cursor/machineId"), 'cursor_path': "/Applications/Cursor.app/Contents/Resources/app", 'updater_path': os.path.expanduser("~/Library/Application Support/cursor-updater"), - 'update_yml_path': "/Applications/Cursor.app/Contents/Resources/app-update.yml" + 'update_yml_path': "/Applications/Cursor.app/Contents/Resources/app-update.yml", + 'product_json_path': "/Applications/Cursor.app/Contents/Resources/app/product.json" } # Create storage directory os.makedirs(os.path.dirname(default_config['MacPaths']['storage_path']), exist_ok=True) @@ -185,7 +187,8 @@ def setup_config(translator=None): 'machine_id_path': os.path.join(cursor_dir, "machineid") if cursor_dir else "", 'cursor_path': get_linux_cursor_path(), 'updater_path': os.path.join(config_base, "cursor-updater"), - 'update_yml_path': os.path.join(cursor_dir, "resources/app-update.yml") if cursor_dir else "" + 'update_yml_path': os.path.join(cursor_dir, "resources/app-update.yml") if cursor_dir else "", + 'product_json_path': os.path.join(cursor_dir, "resources/app/product.json") if cursor_dir else "" } # Read existing configuration and merge diff --git a/disable_auto_update.py b/disable_auto_update.py index f997e4f..b8cc9c1 100644 --- a/disable_auto_update.py +++ b/disable_auto_update.py @@ -34,12 +34,15 @@ class AutoUpdateDisabler: if self.system == "Windows": self.updater_path = config.get('WindowsPaths', 'updater_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater")) self.update_yml_path = config.get('WindowsPaths', 'update_yml_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "update.yml")) + self.product_json_path = config.get('WindowsPaths', 'product_json_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "product.json")) elif self.system == "Darwin": self.updater_path = config.get('MacPaths', 'updater_path', fallback=os.path.expanduser("~/Library/Application Support/cursor-updater")) self.update_yml_path = config.get('MacPaths', 'update_yml_path', fallback="/Applications/Cursor.app/Contents/Resources/app-update.yml") + self.product_json_path = config.get('MacPaths', 'product_json_path', fallback="/Applications/Cursor.app/Contents/Resources/app/product.json") elif self.system == "Linux": self.updater_path = config.get('LinuxPaths', 'updater_path', fallback=os.path.expanduser("~/.config/cursor-updater")) self.update_yml_path = config.get('LinuxPaths', 'update_yml_path', fallback=os.path.expanduser("~/.config/cursor/resources/app-update.yml")) + self.product_json_path = config.get('LinuxPaths', 'product_json_path', fallback=os.path.expanduser("~/.config/cursor/resources/app/product.json")) else: # If configuration loading fails, use default paths self.updater_paths = { @@ -56,21 +59,29 @@ class AutoUpdateDisabler: } self.update_yml_path = self.update_yml_paths.get(self.system) - def _change_main_js(self): - """Change main.js""" + self.product_json_paths = { + "Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "product.json"), + "Darwin": "/Applications/Cursor.app/Contents/Resources/app/product.json", + "Linux": os.path.expanduser("~/.config/cursor/resources/app/product.json") + } + self.product_json_path = self.product_json_paths.get(self.system) + + def _remove_update_url(self): + """Remove update URL""" try: - main_path = get_config(self.translator).get('main_js_path', fallback=os.path.expanduser("~/.config/cursor/resources/app/main.js")) - original_stat = os.stat(main_path) + original_stat = os.stat(self.product_json_path) original_mode = original_stat.st_mode original_uid = original_stat.st_uid original_gid = original_stat.st_gid with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp_file: - with open(main_path, "r", encoding="utf-8") as main_file: - content = main_file.read() + with open(self.product_json_path, "r", encoding="utf-8") as product_json_file: + content = product_json_file.read() patterns = { r"https://api2.cursor.sh/aiserver.v1.AuthService/DownloadUpdate": r"", + r"https://api2.cursor.sh/updates": r"", + r"http://cursorapi.com/updates": r"", } for pattern, replacement in patterns.items(): @@ -79,12 +90,12 @@ class AutoUpdateDisabler: tmp_file.write(content) tmp_path = tmp_file.name - shutil.copy2(main_path, main_path + ".old") - shutil.move(tmp_path, main_path) + shutil.copy2(self.product_json_path, self.product_json_path + ".old") + shutil.move(tmp_path, self.product_json_path) - os.chmod(main_path, original_mode) + os.chmod(self.product_json_path, original_mode) if os.name != "nt": - os.chown(main_path, original_uid, original_gid) + os.chown(self.product_json_path, original_uid, original_gid) print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.file_modified')}{Style.RESET_ALL}") return True @@ -122,17 +133,18 @@ class AutoUpdateDisabler: print(f"{Fore.CYAN}{EMOJI['FOLDER']} {self.translator.get('update.removing_directory') if self.translator else '正在删除更新程序目录...'}{Style.RESET_ALL}") if os.path.exists(updater_path): - if os.path.isdir(updater_path): - shutil.rmtree(updater_path) - else: - os.remove(updater_path) - - print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.directory_removed') if self.translator else '更新程序目录已删除'}{Style.RESET_ALL}") + try: + if os.path.isdir(updater_path): + shutil.rmtree(updater_path) + else: + os.remove(updater_path) + print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.directory_removed') if self.translator else '更新程序目录已删除'}{Style.RESET_ALL}") + except PermissionError: + print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.directory_locked', path=updater_path) if self.translator else f'更新程序目录已被锁定,跳过删除: {updater_path}'}{Style.RESET_ALL}") return True except Exception as e: print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.remove_directory_failed', error=str(e)) if self.translator else f'删除目录失败: {e}'}{Style.RESET_ALL}") - # 即使删除失败,也返回 True,继续执行下一步 return True def _clear_update_yml_file(self): @@ -145,15 +157,15 @@ class AutoUpdateDisabler: print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('update.clearing_update_yml') if self.translator else '正在清空更新配置文件...'}{Style.RESET_ALL}") if os.path.exists(update_yml_path): - # 清空文件内容 - with open(update_yml_path, 'w') as f: - f.write('') - - print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.update_yml_cleared') if self.translator else '更新配置文件已清空'}{Style.RESET_ALL}") - return True + try: + with open(update_yml_path, 'w') as f: + f.write('') + print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.update_yml_cleared') if self.translator else '更新配置文件已清空'}{Style.RESET_ALL}") + except PermissionError: + print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.yml_locked') if self.translator else '更新配置文件已被锁定,跳过清空'}{Style.RESET_ALL}") else: print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.update_yml_not_found') if self.translator else '更新配置文件不存在'}{Style.RESET_ALL}") - return True + return True except Exception as e: print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.clear_update_yml_failed', error=str(e)) if self.translator else f'清空更新配置文件失败: {e}'}{Style.RESET_ALL}") @@ -170,37 +182,43 @@ class AutoUpdateDisabler: print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('update.creating_block_file') if self.translator else '正在创建阻止文件...'}{Style.RESET_ALL}") # 创建 updater_path 阻止文件 - os.makedirs(os.path.dirname(updater_path), exist_ok=True) - open(updater_path, 'w').close() - - # 设置 updater_path 为只读 - if self.system == "Windows": - os.system(f'attrib +r "{updater_path}"') - else: - os.chmod(updater_path, 0o444) # 设置为只读 - - print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.block_file_created') if self.translator else '阻止文件已创建'}: {updater_path}{Style.RESET_ALL}") + try: + os.makedirs(os.path.dirname(updater_path), exist_ok=True) + open(updater_path, 'w').close() + + # 设置 updater_path 为只读 + if self.system == "Windows": + os.system(f'attrib +r "{updater_path}"') + else: + os.chmod(updater_path, 0o444) # 设置为只读 + + print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.block_file_created') if self.translator else '阻止文件已创建'}: {updater_path}{Style.RESET_ALL}") + except PermissionError: + print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.block_file_locked') if self.translator else '阻止文件已被锁定,跳过创建'}{Style.RESET_ALL}") # 检查 update_yml_path update_yml_path = self.update_yml_path if update_yml_path and os.path.exists(os.path.dirname(update_yml_path)): - # 创建 update_yml_path 阻止文件 - with open(update_yml_path, 'w') as f: - f.write('# This file is locked to prevent auto-updates\nversion: 0.0.0\n') - - # 设置 update_yml_path 为只读 - if self.system == "Windows": - os.system(f'attrib +r "{update_yml_path}"') - else: - os.chmod(update_yml_path, 0o444) # 设置为只读 - - print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.yml_locked') if self.translator else '更新配置文件已锁定'}: {update_yml_path}{Style.RESET_ALL}") + try: + # 创建 update_yml_path 阻止文件 + with open(update_yml_path, 'w') as f: + f.write('# This file is locked to prevent auto-updates\nversion: 0.0.0\n') + + # 设置 update_yml_path 为只读 + if self.system == "Windows": + os.system(f'attrib +r "{update_yml_path}"') + else: + os.chmod(update_yml_path, 0o444) # 设置为只读 + + print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.yml_locked') if self.translator else '更新配置文件已锁定'}: {update_yml_path}{Style.RESET_ALL}") + except PermissionError: + print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.yml_already_locked') if self.translator else '更新配置文件已被锁定,跳过修改'}{Style.RESET_ALL}") return True except Exception as e: print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.create_block_file_failed', error=str(e)) if self.translator else f'创建阻止文件失败: {e}'}{Style.RESET_ALL}") - return False + return True # 返回 True 以继续执行后续步骤 def disable_auto_update(self): """Disable auto update""" @@ -222,8 +240,8 @@ class AutoUpdateDisabler: if not self._create_blocking_file(): return False - # 5. Change main.js - if not self._change_main_js(): + # 5. Remove update URL from product.json + if not self._remove_update_url(): return False print(f"{Fore.GREEN}{EMOJI['CHECK']} {self.translator.get('update.disable_success') if self.translator else '自动更新已禁用'}{Style.RESET_ALL}") diff --git a/locales/en.json b/locales/en.json index 6868432..de78d17 100644 --- a/locales/en.json +++ b/locales/en.json @@ -307,7 +307,16 @@ "clear_update_yml_failed": "Failed to clear update.yml file: {error}", "unsupported_os": "Unsupported OS: {system}", "remove_directory_failed": "Failed to remove directory: {error}", - "create_block_file_failed": "Failed to create block file: {error}" + "create_block_file_failed": "Failed to create block file: {error}", + "directory_locked": "Directory is locked: {path}", + "yml_locked": "update.yml file is locked", + "block_file_locked": "block file is locked", + "yml_already_locked": "update.yml file is already locked", + "block_file_already_locked": "block file is already locked", + "block_file_locked_error": "Block file locked error: {error}", + "yml_locked_error": "update.yml file locked error: {error}", + "block_file_already_locked_error": "Block file already locked error: {error}", + "yml_already_locked_error": "update.yml file already locked error: {error}" }, "updater": { "checking": "Checking for updates...", diff --git a/locales/zh_cn.json b/locales/zh_cn.json index f94ce50..c9201df 100644 --- a/locales/zh_cn.json +++ b/locales/zh_cn.json @@ -302,7 +302,16 @@ "clear_update_yml_failed": "清空 update.yml 文件失败: {error}", "unsupported_os": "不支持的操作系统: {system}", "remove_directory_failed": "删除目录失败: {error}", - "create_block_file_failed": "创建阻止文件失败: {error}" + "create_block_file_failed": "创建阻止文件失败: {error}", + "directory_locked": "目录被锁定: {path}", + "yml_locked": "update.yml 文件被锁定", + "block_file_locked": "阻止文件被锁定", + "yml_already_locked": "update.yml 文件已锁定", + "block_file_already_locked": "阻止文件已锁定", + "block_file_locked_error": "阻止文件锁定错误: {error}", + "yml_locked_error": "update.yml 文件锁定错误: {error}", + "block_file_already_locked_error": "阻止文件已锁定错误: {error}", + "yml_already_locked_error": "update.yml 文件已锁定错误: {error}" }, "updater": { "checking": "检查更新...", diff --git a/locales/zh_tw.json b/locales/zh_tw.json index 1748909..4e13511 100644 --- a/locales/zh_tw.json +++ b/locales/zh_tw.json @@ -284,7 +284,16 @@ "clear_update_yml_failed": "清空 update.yml 文件失败: {error}", "unsupported_os": "不支持的操作系统: {system}", "remove_directory_failed": "刪除目錄失败: {error}", - "create_block_file_failed": "創建阻止文件失败: {error}" + "create_block_file_failed": "創建阻止文件失败: {error}", + "directory_locked": "目錄被鎖定: {path}", + "yml_locked": "update.yml 文件被鎖定", + "block_file_locked": "阻止文件被鎖定", + "yml_already_locked": "update.yml 文件已鎖定", + "block_file_already_locked": "阻止文件已鎖定", + "block_file_locked_error": "阻止文件锁定错误: {error}", + "yml_locked_error": "update.yml 文件锁定错误: {error}", + "block_file_already_locked_error": "阻止文件已锁定错误: {error}", + "yml_already_locked_error": "update.yml 文件已锁定错误: {error}" }, "updater": { "checking": "檢查更新...",