Download hermes_cli/claw.py from lenson78/hermes: direct link, hf CLI and curl.
- Browser
- Download file 10.8 kB
-
https://huggingface.co/spaces/lenson78/hermes/resolve/main/hermes_cli/claw.py
- Command line
-
hf download hf://spaces/lenson78/hermes/hermes_cli/claw.py
-
curl -L -o claw.py https://huggingface.co/spaces/lenson78/hermes/resolve/main/hermes_cli/claw.py
10.8 kB
| """hermes claw β OpenClaw migration commands. | |
| Usage: | |
| hermes claw migrate # Interactive migration from ~/.openclaw | |
| hermes claw migrate --dry-run # Preview what would be migrated | |
| hermes claw migrate --preset full --overwrite # Full migration, overwrite conflicts | |
| """ | |
| import importlib.util | |
| import logging | |
| import sys | |
| from pathlib import Path | |
| from hermes_cli.config import get_hermes_home, get_config_path, load_config, save_config | |
| from hermes_cli.setup import ( | |
| Colors, | |
| color, | |
| print_header, | |
| print_info, | |
| print_success, | |
| print_warning, | |
| print_error, | |
| prompt_yes_no, | |
| prompt_choice, | |
| ) | |
| logger = logging.getLogger(__name__) | |
| PROJECT_ROOT = Path(__file__).parent.parent.resolve() | |
| _OPENCLAW_SCRIPT = ( | |
| PROJECT_ROOT | |
| / "optional-skills" | |
| / "migration" | |
| / "openclaw-migration" | |
| / "scripts" | |
| / "openclaw_to_hermes.py" | |
| ) | |
| # Fallback: user may have installed the skill from the Hub | |
| _OPENCLAW_SCRIPT_INSTALLED = ( | |
| get_hermes_home() | |
| / "skills" | |
| / "migration" | |
| / "openclaw-migration" | |
| / "scripts" | |
| / "openclaw_to_hermes.py" | |
| ) | |
| def _find_migration_script() -> Path | None: | |
| """Find the openclaw_to_hermes.py script in known locations.""" | |
| for candidate in [_OPENCLAW_SCRIPT, _OPENCLAW_SCRIPT_INSTALLED]: | |
| if candidate.exists(): | |
| return candidate | |
| return None | |
| def _load_migration_module(script_path: Path): | |
| """Dynamically load the migration script as a module.""" | |
| spec = importlib.util.spec_from_file_location("openclaw_to_hermes", script_path) | |
| if spec is None or spec.loader is None: | |
| return None | |
| mod = importlib.util.module_from_spec(spec) | |
| # Register in sys.modules so @dataclass can resolve the module | |
| # (Python 3.11+ requires this for dynamically loaded modules) | |
| sys.modules[spec.name] = mod | |
| try: | |
| spec.loader.exec_module(mod) | |
| except Exception: | |
| sys.modules.pop(spec.name, None) | |
| raise | |
| return mod | |
| def claw_command(args): | |
| """Route hermes claw subcommands.""" | |
| action = getattr(args, "claw_action", None) | |
| if action == "migrate": | |
| _cmd_migrate(args) | |
| else: | |
| print("Usage: hermes claw migrate [options]") | |
| print() | |
| print("Commands:") | |
| print(" migrate Migrate settings from OpenClaw to Hermes") | |
| print() | |
| print("Run 'hermes claw migrate --help' for migration options.") | |
| def _cmd_migrate(args): | |
| """Run the OpenClaw β Hermes migration.""" | |
| source_dir = Path(getattr(args, "source", None) or Path.home() / ".openclaw") | |
| dry_run = getattr(args, "dry_run", False) | |
| preset = getattr(args, "preset", "full") | |
| overwrite = getattr(args, "overwrite", False) | |
| migrate_secrets = getattr(args, "migrate_secrets", False) | |
| workspace_target = getattr(args, "workspace_target", None) | |
| skill_conflict = getattr(args, "skill_conflict", "skip") | |
| # If using the "full" preset, secrets are included by default | |
| if preset == "full": | |
| migrate_secrets = True | |
| print() | |
| print( | |
| color( | |
| "βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ", | |
| Colors.MAGENTA, | |
| ) | |
| ) | |
| print( | |
| color( | |
| "β β Hermes β OpenClaw Migration β", | |
| Colors.MAGENTA, | |
| ) | |
| ) | |
| print( | |
| color( | |
| "βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ", | |
| Colors.MAGENTA, | |
| ) | |
| ) | |
| # Check source directory | |
| if not source_dir.is_dir(): | |
| print() | |
| print_error(f"OpenClaw directory not found: {source_dir}") | |
| print_info("Make sure your OpenClaw installation is at the expected path.") | |
| print_info(f"You can specify a custom path: hermes claw migrate --source /path/to/.openclaw") | |
| return | |
| # Find the migration script | |
| script_path = _find_migration_script() | |
| if not script_path: | |
| print() | |
| print_error("Migration script not found.") | |
| print_info("Expected at one of:") | |
| print_info(f" {_OPENCLAW_SCRIPT}") | |
| print_info(f" {_OPENCLAW_SCRIPT_INSTALLED}") | |
| print_info("Make sure the openclaw-migration skill is installed.") | |
| return | |
| # Show what we're doing | |
| hermes_home = get_hermes_home() | |
| print() | |
| print_header("Migration Settings") | |
| print_info(f"Source: {source_dir}") | |
| print_info(f"Target: {hermes_home}") | |
| print_info(f"Preset: {preset}") | |
| print_info(f"Mode: {'dry run (preview only)' if dry_run else 'execute'}") | |
| print_info(f"Overwrite: {'yes' if overwrite else 'no (skip conflicts)'}") | |
| print_info(f"Secrets: {'yes (allowlisted only)' if migrate_secrets else 'no'}") | |
| if skill_conflict != "skip": | |
| print_info(f"Skill conflicts: {skill_conflict}") | |
| if workspace_target: | |
| print_info(f"Workspace: {workspace_target}") | |
| print() | |
| # For execute mode (non-dry-run), confirm unless --yes was passed | |
| if not dry_run and not getattr(args, "yes", False): | |
| if not prompt_yes_no("Proceed with migration?", default=True): | |
| print_info("Migration cancelled.") | |
| return | |
| # Ensure config.yaml exists before migration tries to read it | |
| config_path = get_config_path() | |
| if not config_path.exists(): | |
| save_config(load_config()) | |
| # Load and run the migration | |
| try: | |
| mod = _load_migration_module(script_path) | |
| if mod is None: | |
| print_error("Could not load migration script.") | |
| return | |
| selected = mod.resolve_selected_options(None, None, preset=preset) | |
| ws_target = Path(workspace_target).resolve() if workspace_target else None | |
| migrator = mod.Migrator( | |
| source_root=source_dir.resolve(), | |
| target_root=hermes_home.resolve(), | |
| execute=not dry_run, | |
| workspace_target=ws_target, | |
| overwrite=overwrite, | |
| migrate_secrets=migrate_secrets, | |
| output_dir=None, | |
| selected_options=selected, | |
| preset_name=preset, | |
| skill_conflict_mode=skill_conflict, | |
| ) | |
| report = migrator.migrate() | |
| except Exception as e: | |
| print() | |
| print_error(f"Migration failed: {e}") | |
| logger.debug("OpenClaw migration error", exc_info=True) | |
| return | |
| # Print results | |
| _print_migration_report(report, dry_run) | |
| def _print_migration_report(report: dict, dry_run: bool): | |
| """Print a formatted migration report.""" | |
| summary = report.get("summary", {}) | |
| migrated = summary.get("migrated", 0) | |
| skipped = summary.get("skipped", 0) | |
| conflicts = summary.get("conflict", 0) | |
| errors = summary.get("error", 0) | |
| total = migrated + skipped + conflicts + errors | |
| print() | |
| if dry_run: | |
| print_header("Dry Run Results") | |
| print_info("No files were modified. This is a preview of what would happen.") | |
| else: | |
| print_header("Migration Results") | |
| print() | |
| # Detailed items | |
| items = report.get("items", []) | |
| if items: | |
| # Group by status | |
| migrated_items = [i for i in items if i.get("status") == "migrated"] | |
| skipped_items = [i for i in items if i.get("status") == "skipped"] | |
| conflict_items = [i for i in items if i.get("status") == "conflict"] | |
| error_items = [i for i in items if i.get("status") == "error"] | |
| if migrated_items: | |
| label = "Would migrate" if dry_run else "Migrated" | |
| print(color(f" β {label}:", Colors.GREEN)) | |
| for item in migrated_items: | |
| kind = item.get("kind", "unknown") | |
| dest = item.get("destination", "") | |
| if dest: | |
| dest_short = str(dest).replace(str(Path.home()), "~") | |
| print(f" {kind:<22s} β {dest_short}") | |
| else: | |
| print(f" {kind}") | |
| print() | |
| if conflict_items: | |
| print(color(f" β Conflicts (skipped β use --overwrite to force):", Colors.YELLOW)) | |
| for item in conflict_items: | |
| kind = item.get("kind", "unknown") | |
| reason = item.get("reason", "already exists") | |
| print(f" {kind:<22s} {reason}") | |
| print() | |
| if skipped_items: | |
| print(color(f" β Skipped:", Colors.DIM)) | |
| for item in skipped_items: | |
| kind = item.get("kind", "unknown") | |
| reason = item.get("reason", "") | |
| print(f" {kind:<22s} {reason}") | |
| print() | |
| if error_items: | |
| print(color(f" β Errors:", Colors.RED)) | |
| for item in error_items: | |
| kind = item.get("kind", "unknown") | |
| reason = item.get("reason", "unknown error") | |
| print(f" {kind:<22s} {reason}") | |
| print() | |
| # Summary line | |
| parts = [] | |
| if migrated: | |
| action = "would migrate" if dry_run else "migrated" | |
| parts.append(f"{migrated} {action}") | |
| if conflicts: | |
| parts.append(f"{conflicts} conflict(s)") | |
| if skipped: | |
| parts.append(f"{skipped} skipped") | |
| if errors: | |
| parts.append(f"{errors} error(s)") | |
| if parts: | |
| print_info(f"Summary: {', '.join(parts)}") | |
| else: | |
| print_info("Nothing to migrate.") | |
| # Output directory | |
| output_dir = report.get("output_dir") | |
| if output_dir: | |
| print_info(f"Full report saved to: {output_dir}") | |
| if dry_run: | |
| print() | |
| print_info("To execute the migration, run without --dry-run:") | |
| print_info(f" hermes claw migrate --preset {report.get('preset', 'full')}") | |
| elif migrated: | |
| print() | |
| print_success("Migration complete!") | |
| # Warn if API keys were skipped (migrate_secrets not enabled) | |
| skipped_keys = [ | |
| i for i in report.get("items", []) | |
| if i.get("kind") == "provider-keys" and i.get("status") == "skipped" | |
| ] | |
| if skipped_keys: | |
| print() | |
| print(color(" β API keys were NOT migrated (secrets migration is disabled by default).", Colors.YELLOW)) | |
| print(color(" Your OPENROUTER_API_KEY and other provider keys must be added manually.", Colors.YELLOW)) | |
| print() | |
| print_info("To migrate API keys, re-run with:") | |
| print_info(" hermes claw migrate --migrate-secrets") | |
| print() | |
| print_info("Or add your key manually:") | |
| print_info(" hermes config set OPENROUTER_API_KEY sk-or-v1-...") | |