文件预览

uninstall.py

查看 EvoMap WorkBench v1.0.11 Mini 技能包中的文件内容。

文件内容

uninstall.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
EvoMap WorkBench v1.0.11 - 卸载脚本

负责安全地从 OpenClaw 中移除 EvoMap WorkBench,
并恢复被修改的 3 个 JSON 文件到原始状态。

版本:v1.0.11
创建时间:2026-04-06
"""

import json
import os
import shutil
from pathlib import Path
from typing import Dict, List, Optional
import sys


class Uninstaller:
    """EvoMap WorkBench 卸载器"""
    
    def __init__(self):
        self.base_path = Path.home() / ".openclaw"
        self.installed_skill_path = None
        self.feishu_credentials_path = None
        
        # 定义需要恢复的 JSON 文件
        self.files_to_restore = {
            "skills-index.json": None,  # 技能索引表
            "feishu-pairing.json": None,  # 飞书凭证
            "skills-config": None  # 技能实例配置目录
        }
        
    def find_installed_skill(self) -> Optional[Path]:
        """查找已安装的 EvoMap WorkBench"""
        possible_paths = [
            "/home/admin/.openclaw/workspace/skills/evomap-workbench",
            "/home/admin/.openclaw/workspace/ai 知识变现/evomap 项目/skills/evomap-workbench",
        ]
        
        for path in possible_paths:
            if Path(path).exists():
                self.installed_skill_path = Path(path)
                return self.installed_skill_path
        
        print("⚠️ 未找到已安装的 EvoMap WorkBench")
        return None
    
    def load_json_file(self, file_path: Path) -> Dict:
        """加载 JSON 文件"""
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                return json.load(f)
        except FileNotFoundError:
            return {}
        except json.JSONDecodeError as e:
            print(f"❌ JSON 解析错误:{e}")
            return {}
    
    def save_json_file(self, file_path: Path, data: Dict):
        """保存 JSON 文件"""
        try:
            file_path.parent.mkdir(parents=True, exist_ok=True)
            with open(file_path, 'w', encoding='utf-8') as f:
                json.dump(data, f, indent=2, ensure_ascii=False)
            return True
        except Exception as e:
            print(f"❌ 保存失败:{e}")
            return False
    
    def restore_skills_index(self) -> bool:
        """恢复 skills-index.json"""
        index_path = self.base_path / "openclaw.json"
        
        if not index_path.exists():
            print("✅ skills-index.json 不存在,无需恢复")
            return True
        
        print("\n📋 处理 skills-index.json...")
        
        # 加载当前配置
        config = self.load_json_file(index_path)
        
        # 检查是否包含 EvoMap WorkBench
        installed_skills = config.get("installed_skills", [])
        original_length = len(installed_skills)
        
        # 过滤掉 EvoMap WorkBench
        new_skills = [
            skill for skill in installed_skills 
            if skill.get("skill_id") != "evomap-workbench"
        ]
        
        if len(new_skills) < original_length:
            # 找到了 EvoMap WorkBench,删除它
            config["installed_skills"] = new_skills
            if self.save_json_file(index_path, config):
                print(f"✅ 已从注册表移除 (原 {original_length} 项 → 现 {len(new_skills)} 项)")
                return True
            else:
                return False
        else:
            print("✅ EvoMap WorkBench 不在注册表中,无需恢复")
            return True
    
    def restore_feishu_config(self, remove_app_config: bool = True) -> bool:
        """恢复 feishu-pairing.json"""
        credentials_path = self.base_path / "credentials" / "feishu-pairing.json"
        
        if not credentials_path.exists():
            print("✅ feishu-pairing.json 不存在,无需恢复")
            return True
        
        print("\n🔧 处理 feishu-pairing.json...")
        
        # 加载当前配置
        config = self.load_json_file(credentials_path)
        
        # 检查是否包含 EvoMap 相关配置
        evomap_keys = ["evomap_appId", "evomap_appSecret", "cli_a929676f8bf81cc7"]
        keys_to_check = []
        
        for key in config.keys():
            for evomap_key in evomap_keys:
                if evomap_key.lower() in key.lower():
                    keys_to_check.append(key)
        
        if keys_to_check and remove_app_config:
            # 用户确认删除,移除相关字段
            for key in keys_to_check:
                del config[key]
            
            if self.save_json_file(credentials_path, config):
                print(f"✅ 已移除 EvoMap 相关配置 ({len(keys_to_check)} 个字段)")
                return True
            else:
                return False
        elif keys_to_check and not remove_app_config:
            print(f"ℹ️ 发现 {len(keys_to_check)} 个 EvoMap 相关字段,但未执行删除")
            return True
        else:
            print("✅ feishu-pairing.json 中无 EvoMap 配置,无需恢复")
            return True
    
    def remove_skills_config(self) -> bool:
        """删除技能实例配置文件"""
        config_dir = self.base_path / "config" / "skills"
        
        if not config_dir.exists():
            print("✅ 技能配置目录不存在,无需恢复")
            return True
        
        print("\n🗑️ 处理技能实例配置...")
        
        # 查找所有与 evomap 相关的配置文件
        evomap_configs = []
        for config_file in config_dir.iterdir():
            if config_file.is_file() and ("evomap" in config_file.name.lower() or "workbench" in config_file.name.lower()):
                evomap_configs.append(config_file)
        
        if evomap_configs:
            for config_file in evomap_configs:
                try:
                    config_file.unlink()
                    print(f"✅ 已删除:{config_file.name}")
                except Exception as e:
                    print(f"⚠️ 无法删除 {config_file.name}: {e}")
            return True
        else:
            print("✅ 技能配置目录中无 EvoMap 配置文件,无需恢复")
            return True
    
    def remove_installed_skill(self) -> bool:
        """删除已安装的技能实例"""
        if not self.installed_skill_path or not self.installed_skill_path.exists():
            print("✅ 已安装的 EvoMap WorkBench 不存在,无需删除")
            return True
        
        print(f"\n🗑️ 删除已安装的技能实例:{self.installed_skill_path}")
        
        try:
            shutil.rmtree(self.installed_skill_path)
            print(f"✅ 已删除:{self.installed_skill_path}")
            return True
        except Exception as e:
            print(f"❌ 删除失败:{e}")
            return False
    
    def uninstall(self, confirm_feishu_removal: bool = True) -> bool:
        """
        执行完整卸载流程
        
        Args:
            confirm_feishu_removal: 是否删除飞书配置
            
        Returns:
            是否成功卸载
        """
        print("=" * 70)
        print("🧬 EvoMap WorkBench v1.0.11 - 卸载程序")
        print("=" * 70)
        print()
        
        # 步骤 1: 查找已安装的技能
        print("步骤 1/5: 检查已安装技能...")
        if not self.find_installed_skill():
            print("❌ 无法继续:未找到已安装的 EvoMap WorkBench")
            return False
        print(f"✅ 找到:{self.installed_skill_path}")
        
        # 步骤 2: 询问用户是否要删除飞书配置
        print("\n步骤 2/5: 确认飞书配置处理方式...")
        if confirm_feishu_removal:
            response = input("是否删除 EvoMap WorkBench 使用的飞书 App 配置?(y/n): ")
            if response.lower() == 'y':
                confirm_feishu_removal = True
            else:
                confirm_feishu_removal = False
                print("ℹ️ 将保留飞书配置中的 EvoMap 相关字段")
        print(f"配置处理:{'删除' if confirm_feishu_removal else '保留'}")
        
        # 步骤 3: 从 OpenClaw 注册表移除
        print("\n步骤 3/5: 从注册表移除...")
        if not self.restore_skills_index():
            print("❌ 注册表恢复失败")
            return False
        print("✅ 注册表恢复完成")
        
        # 步骤 4: 恢复飞书配置
        print("\n步骤 4/5: 恢复飞书配置...")
        if not self.restore_feishu_config(confirm_feishu_removal):
            print("❌ 飞书配置恢复失败")
            return False
        print("✅ 飞书配置恢复完成")
        
        # 步骤 5: 删除技能配置文件和实例
        print("\n步骤 5/5: 清理文件...")
        if not self.remove_skills_config():
            print("⚠️ 技能配置清理部分完成")
        
        if not self.remove_installed_skill():
            print("❌ 技能实例删除失败")
            return False
        
        print("\n" + "=" * 70)
        print("🎉 EvoMap WorkBench 已成功卸载!")
        print("=" * 70)
        print("\n已恢复的文件:")
        print("  ✅ skills-index.json - 从注册表中移除")
        print("  ✅ feishu-pairing.json - 清除 EvoMap 相关配置" if confirm_feishu_removal else "  ⚠️ feishu-pairing.json - 保留 EvoMap 配置")
        print("  ✅ 技能配置文件 - 已删除")
        print("\n如需重新安装,请运行安装脚本。")
        
        return True


def main():
    """主函数"""
    import argparse
    
    parser = argparse.ArgumentParser(description="EvoMap WorkBench v1.0.11 - 卸载工具")
    parser.add_argument("--keep-feishu", action="store_true", 
                        help="保留飞书配置中的 EvoMap 相关字段")
    parser.add_argument("--force", action="store_true", 
                        help="强制卸载,不提示")
    
    args = parser.parse_args()
    
    uninstaller = Uninstaller()
    
    if args.force:
        success = uninstaller.uninstall(confirm_feishu_removal=not args.keep_feishu)
    else:
        success = uninstaller.uninstall(confirm_feishu_removal=True)
    
    sys.exit(0 if success else 1)


if __name__ == "__main__":
    main()