文件预览

install.py

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

文件内容

install.py

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

负责将 EvoMap WorkBench 安装到 OpenClaw,
并自动处理 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 Installer:
    """EvoMap WorkBench 安装器"""
    
    def __init__(self, source_path: str = None):
        self.base_path = Path.home() / ".openclaw"
        
        # 默认源路径:发布包位置
        if source_path:
            self.source_path = Path(source_path)
        else:
            self.source_path = Path("/home/admin/.openclaw/workspace/ai 知识变现/evomap 项目/skills/evomap-workbench-release")
        
        self.target_path = self.base_path / "workspace" / "skills" / "evomap-workbench"
        
        # 定义要处理的 JSON 文件
        self.json_files_to_process = {
            "skill_metadata.json": "update",  # 更新版本和路径
            "skills-index.json": "read_write",  # 读取并写入注册表
            "feishu-pairing.json": "optional_read_write"  # 可选读取飞书配置
        }
    
    def check_dependencies(self) -> bool:
        """检查依赖"""
        required = ["python3", "pip"]
        missing = []
        
        for dep in required:
            if not shutil.which(dep):
                missing.append(dep)
        
        if missing:
            print(f"❌ 缺少必需依赖:{', '.join(missing)}")
            return False
        
        print("✅ 所有依赖已满足")
        return True
    
    def check_source(self) -> bool:
        """检查源路径"""
        if not self.source_path.exists():
            print(f"❌ 源路径不存在:{self.source_path}")
            return False
        
        required_files = ["lib/", "docs/", "SKILL.md", "skill_metadata.json"]
        missing = [f for f in required_files if not (self.source_path / f).exists()]
        
        if missing:
            print(f"❌ 源目录缺少必要文件:{', '.join(missing)}")
            return False
        
        print("✅ 源路径验证通过")
        return True
    
    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 process_skill_metadata(self) -> bool:
        """处理 skill_metadata.json"""
        metadata_path = self.source_path / "skill_metadata.json"
        
        if not metadata_path.exists():
            print("⚠️ skill_metadata.json 不存在,跳过")
            return True
        
        print("\n📝 处理 skill_metadata.json...")
        
        try:
            # 读取并发布包内文件内容(不做修改,只验证)
            metadata = self.load_json_file(metadata_path)
            
            # 打印当前版本信息
            version = metadata.get("version", "unknown")
            print(f"   版本:v{version}")
            print(f"   作者:{metadata.get('author', 'unknown')}")
            print(f"   描述:{metadata.get('description', '')[:50]}...")
            
            return True
        except Exception as e:
            print(f"❌ 处理失败:{e}")
            return False
    
    def update_skills_index(self) -> bool:
        """更新 skills-index.json"""
        index_path = self.base_path / "openclaw.json"
        
        print("\n📋 更新 skills-index.json...")
        
        # 加载或创建索引
        if index_path.exists():
            config = self.load_json_file(index_path)
        else:
            config = {"installed_skills": [], "last_updated": None}
            print("ℹ️ 创建新的技能索引文件")
        
        # 检查是否已安装
        installed_skills = config.get("installed_skills", [])
        for skill in installed_skills:
            if skill.get("skill_id") == "evomap-workbench":
                print("✅ EvoMap WorkBench 已安装,跳过")
                return True
        
        # 添加新技能到注册表
        new_skill_entry = {
            "skill_id": "evomap-workbench",
            "path": str(self.target_path),
            "version": "v1.0.11",
            "name": "EvoMap WorkBench",
            "status": "active",
            "installed_at": None,  # 会在安装时填充时间戳
            "dependencies": metadata.get("dependencies", [])
        }
        
        installed_skills.append(new_skill_entry)
        config["installed_skills"] = installed_skills
        config["last_updated"] = None  # 会更新为当前时间
        
        if self.save_json_file(index_path, config):
            print("✅ 已成功添加到技能注册表")
            return True
        else:
            return False
    
    def read_feishu_config(self) -> Optional[Dict]:
        """读取 feishu-pairing.json"""
        credentials_path = self.base_path / "credentials" / "feishu-pairing.json"
        
        if not credentials_path.exists():
            return None
        
        print("\n🔧 读取 feishu-pairing.json...")
        config = self.load_json_file(credentials_path)
        
        # 检查是否有相关配置
        app_id_keys = [k for k in config.keys() if "app" in k.lower() and ("id" in k.lower() or "secret" in k.lower())]
        
        if app_id_keys:
            print(f"   发现 {len(app_id_keys)} 个 App 配置字段")
            
            # 提取关键信息(不输出敏感信息)
            for key in app_id_keys:
                if "id" in key.lower():
                    print(f"   ✅ App ID: {config[key][:10]}...")
                elif "secret" in key.lower():
                    print(f"   🔒 App Secret: ***")
        else:
            print("   ⚠️ 未检测到 App 配置")
        
        return config
    
    def copy_skills(self) -> bool:
        """复制技能文件到目标位置"""
        print(f"\n📦 复制文件到:{self.target_path}")
        
        try:
            # 创建目标目录
            self.target_path.parent.mkdir(parents=True, exist_ok=True)
            
            # 复制所有文件和目录
            for item in self.source_path.iterdir():
                dest = self.target_path / item.name
                
                if item.is_dir():
                    if dest.exists():
                        shutil.rmtree(dest)
                    shutil.copytree(item, dest)
                else:
                    shutil.copy2(item, dest)
            
            print("✅ 文件复制完成")
            return True
        except Exception as e:
            print(f"❌ 复制失败:{e}")
            return False
    
    def create_symlink_if_needed(self) -> bool:
        """如需要则创建符号链接"""
        # 目前不需要,但保留扩展点
        return True
    
    def install(self, skip_check: bool = False) -> bool:
        """执行完整安装流程"""
        print("=" * 70)
        print("🧬 EvoMap WorkBench v1.0.11 - 安装工具")
        print("=" * 70)
        print()
        
        # 步骤 1: 检查依赖
        if not skip_check:
            print("步骤 1/5: 检查环境...")
            if not self.check_dependencies():
                return False
        
        # 步骤 2: 检查源路径
        print("\n步骤 2/5: 验证源目录...")
        if not self.check_source():
            return False
        
        # 步骤 3: 处理 skill_metadata.json
        print("\n步骤 3/5: 处理元数据...")
        if not self.process_skill_metadata():
            return False
        
        # 步骤 4: 读取飞书配置(仅读取,不修改)
        print("\n步骤 4/5: 检查现有配置...")
        feishu_config = self.read_feishu_config()
        
        # 步骤 5: 复制文件并更新注册表
        print("\n步骤 5/5: 执行安装...")
        if not self.copy_skills():
            print("❌ 文件复制失败")
            return False
        
        if not self.update_skills_index():
            print("❌ 注册表更新失败")
            return False
        
        print("\n" + "=" * 70)
        print("🎉 EvoMap WorkBench v1.0.11 已成功安装!")
        print("=" * 70)
        print("\n安装位置:")
        print(f"   {self.target_path}")
        print("\n已更新的文件:")
        print("   ✅ skill_metadata.json - 已验证")
        print("   ✅ skills-index.json - 新增注册条目")
        print(f"   ℹ️ feishu-pairing.json - 已检查 ({'有配置' if feishu_config else '无配置'})")
        print("\n如需卸载,请运行:python3 uninstall.py")
        
        return True


def main():
    """主函数"""
    import argparse
    
    parser = argparse.ArgumentParser(description="EvoMap WorkBench v1.0.11 - 安装工具")
    parser.add_argument("--source", type=str, default=None,
                        help="源路径(默认为发布包位置)")
    parser.add_argument("--skip-check", action="store_true",
                        help="跳过环境检查")
    
    args = parser.parse_args()
    
    installer = Installer(source_path=args.source)
    success = installer.install(skip_check=args.skip_check)
    
    sys.exit(0 if success else 1)


if __name__ == "__main__":
    main()