Skill: vegetable1bird/realmrouter-switch (version 2.1.0)

--- name: realmrouter-switch description: Zero-friction RealmRouter model manager for OpenClaw. Chat-first workflow for setting API key, guided model picking, switching with availability check, rollback, connectivity testing, and short rr commands on both Unix and Windows. user-invocable: true metadata: openclaw: requires: bins: - openclaw env: - REALMROUTER_API_KEY primaryEnv: REALMROUTER_API_KEY emoji: "🔁" os: - darwin - linux - win32 version: 2.1.0 --- # RealmRouter Switch > Chat-first model switching for OpenClaw. > 面向 OpenClaw 的对话式模型切换工具。 ## 操作逻辑 ### 1. 对话模式（推荐） 安装 skill 后，你只需要直接说： - “切换模型” → 我会列出可用模型，你回复编号或模型名 - “把 key 设为 xxx” → 我会验证并更新 - “测试连通性” → 我会检查当前配置是否正常 - “回滚到上一个备份” → 我会恢复最近的备份 **重要**：所有操作我都会自动帮你执行，包括重启 gateway。 ### 2. 命令行模式 如果习惯命令行，可以用 `rr` 快捷命令： - `rr show` - 查看当前状态 - `rr pick` - 列出编号模型列表 - `rr switch <model>` - 切换模型 - `rr key <key>` - 更新 API Key - `rr test` - 测试连通性 - `rr rollback` - 回滚备份 --- ## 安装指南 ### macOS / Linux 安装 #### 步骤 1: 安装 skill ```bash npx clawhub install realmrouter-switch --force ``` #### 步骤 2: 安装 rr 快捷命令（可选但推荐） ```bash # 方式一：使用安装脚本 bash ~/.openclaw/workspace/skills/realmrouter-switch/scripts/install_rr.sh # 方式二：手动添加到 PATH echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc # 如果用 zsh echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc # 如果用 bash source ~/.zshrc # 或 source ~/.bashrc ``` #### 步骤 3: 验证安装 ```bash rr show # 或直接用 Python 脚本 python3 ~/.openclaw/workspace/skills/realmrouter-switch/scripts/realm_manager.py show ``` --- ### Windows 安装 #### 步骤 1: 安装 skill ```powershell npx clawhub install realmrouter-switch --force ``` #### 步骤 2: 安装 rr 快捷命令 ```powershell # 以管理员权限运行 PowerShell，然后执行： powershell -ExecutionPolicy Bypass -File "$env:USERPROFILE\.openclaw\workspace\skills\realmrouter-switch\scripts\install_rr.ps1" ``` #### 步骤 3: 重启终端 关闭当前 PowerShell 窗口，重新打开一个新的。 #### 步骤 4: 验证安装 ```powershell rr show # 或直接调用脚本 powershell -ExecutionPolicy Bypass -File "$env:USERPROFILE\.openclaw\workspace\skills\realmrouter-switch\scripts\realm_manager.ps1" show ``` --- ## 命令详解 ### rr show 显示当前配置状态： - RealmRouter provider 是否存在 - 当前默认模型 - API Key（已掩码） ### rr pick 列出编号模型列表，方便选择： ``` [1] claude-opus-4-6-thinking [2] claude-sonnet-4.6 [3] gpt-5.3-codex [4] gpt-5.2-codex [5] qwen3-max ... ``` ### rr switch <model> 切换默认模型。支持三种格式： - **编号**: `rr switch 3` （先用 `rr pick` 查看编号） - **别名**: `rr switch gpt53` （内置别名见下方） - **完整 ID**: `rr switch gpt-5.3-codex` **内置别名**： | 别名 | 实际模型 | |------|---------| | `opus` | claude-opus-4-6-thinking | | `sonnet` | claude-sonnet-4.6 | | `gpt53` | gpt-5.3-codex | | `gpt52` | gpt-5.2-codex | | `qwen` | qwen3-max | | `r1` | deepseek-ai/DeepSeek-R1 | | `gemini` | gemini-3.1-pro-high | | `glm5` | zai-org/GLM-5 | ### rr key <api-key> 更新 RealmRouter API Key，会自动验证有效性。 ### rr test 测试当前配置的连通性，验证 Key 和模型是否可用。 ### rr rollback 恢复最近的备份文件。 ### rr menu (Windows) 进入交互菜单模式，适合不熟悉命令行的用户。 --- ## 完整使用流程示例 ### macOS / Linux ```bash # 1. 安装 npx clawhub install realmrouter-switch --force bash ~/.openclaw/workspace/skills/realmrouter-switch/scripts/install_rr.sh # 2. 设置 Key rr key sk-xxxxxxxxxxxxx # 3. 查看可选模型 rr pick # 4. 切换模型 rr switch 3 # 或 rr switch gpt53 # 5. 验证 rr test rr show ``` ### Windows ```powershell # 1. 安装 npx clawhub install realmrouter-switch --force powershell -ExecutionPolicy Bypass -File "$env:USERPROFILE\.openclaw\workspace\skills\realmrouter-switch\scripts\install_rr.ps1" # 2. 重启终端 # 3. 设置 Key rr key sk-xxxxxxxxxxxxx # 4. 查看可选模型 rr pick # 5. 切换模型 rr switch 3 # 或 rr switch gpt53 # 6. 验证 rr test rr show ``` --- ## 特性总结 - **安全**: 修改前自动备份到 `~/.openclaw/backups/` - **验证**: Key 和模型都会验证可用性 - **便捷**: 支持编号、别名、完整 ID 三种方式 - **跨平台**: macOS / Linux / Windows 统一体验 - **对话优先**: 直接告诉我你要做什么，我来执行 --- ## 故障排除 ### `rr: command not found` - macOS/Linux: 确保 `~/.local/bin` 在 PATH 中 - Windows: 确保已重启终端，且 `%USERPROFILE%\bin` 在 PATH 中 ### `API key verification failed` - 检查 Key 是否正确 - 检查网络是否能访问 `https://realmrouter.cn/v1` - 使用 `-Force` 参数强制跳过验证 ### 切换后模型没生效 - 确保重启了 gateway: `openclaw gateway restart` - 或在命令中加 `-RestartGateway` 参数

path: _meta.json
content: ownerId: kn7b8bn03kecka53hkf5f7sk0n81z065
slug: realmrouter-switch
version: 1.7.0
publishedAt: 1772245325829

path: scripts/install_rr.sh
content: #!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
RR_SRC="$SCRIPT_DIR/rr.sh"
TARGET_DIR="${HOME}/.local/bin"
TARGET="$TARGET_DIR/rr"

mkdir -p "$TARGET_DIR"
cp "$RR_SRC" "$TARGET"
chmod +x "$TARGET"

echo "✅ Installed rr to: $TARGET"
echo "If needed, add to PATH: export PATH=\"$TARGET_DIR:\$PATH\""


path: scripts/install_rr_win.py
content: #!/usr/bin/env python3
"""Install Windows rr command without PowerShell scripts.
Creates %USERPROFILE%\bin\rr.cmd and copies rr_win.py there.
"""

from __future__ import annotations

import os
import shutil
from pathlib import Path

script_dir = Path(__file__).resolve().parent
src = script_dir / "rr_win.py"
if not src.exists():
    raise SystemExit(f"rr_win.py not found: {src}")

home = Path.home()
target_dir = home / "bin"
target_dir.mkdir(parents=True, exist_ok=True)

target_py = target_dir / "rr_win.py"
shutil.copy2(src, target_py)

rr_cmd = target_dir / "rr.cmd"
rr_cmd.write_text('@echo off\r\npy -3 "%~dp0rr_win.py" %*\r\n', encoding="ascii")

user_path = os.environ.get("Path", "")
if str(target_dir).lower() not in user_path.lower():
    print(f"Add to your User PATH: {target_dir}")

print(f"OK: installed {target_py}")
print(f"OK: installed {rr_cmd}")
print("Reopen terminal, then run: rr show")


path: scripts/realm_manager.py
content: #!/usr/bin/env python3
"""
RealmRouter model switcher for OpenClaw.
"""

from __future__ import annotations

import argparse
import datetime as dt
import json
import os
import shutil
import subprocess
import sys
from pathlib import Path
from urllib import error, request

API_BASE = "https://realmrouter.cn/v1"
DEFAULT_CONFIG = Path.home() / ".openclaw" / "openclaw.json"
BACKUP_DIR = Path.home() / ".openclaw" / "backups"
KEY_ENV = "REALMROUTER_API_KEY"

MODELS = {
    "anthropic": [
        "claude-haiku-4.5",
        "claude-opus-4-5-thinking",
        "claude-opus-4-6-thinking",
        "claude-sonnet-4-5",
        "claude-opus-4.6",
        "claude-sonnet-4.6",
    ],
    "openai": ["gpt-5.2", "gpt-5.2-codex", "gpt-5.3-codex", "openai/gpt-oss-120b"],
    "qwen": [
        "qwen3-max",
        "qwen3-coder-plus",
        "qwen3-max-preview",
        "qwen3-vl-plus",
        "qwen3-vl-max",
        "Qwen/Qwen3.5",
        "Qwen/Qwen3-Coder-Next",
        [REDACTED_SECRET],
    ],
    "deepseek": [
        "deepseek-ai/DeepSeek-R1",
        "deepseek-ai/DeepSeek-R1-0528",
        "deepseek-ai/DeepSeek-V3.1",
        "deepseek-ai/DeepSeek-V3.1-Terminus",
        "deepseek-ai/DeepSeek-V3.2-Exp",
    ],
    "google": ["gemini-3.1-pro-high", "gemini-3.1-pro-low"],
    "minimax": ["MiniMaxAI/MiniMax-M2.1", "MiniMaxAI/MiniMax-M2.5"],
    "moonshot": ["moonshotai/Kimi-K2.5", "moonshotai/Kimi-K2-Thinking"],
    "bytedance": ["doubao-seed-code-preview-251028"],
    "zai": ["zai-org/GLM-4.7", "zai-org/GLM-4.6V", "zai-org/GLM-5"],
}

PREFERRED = [
    "claude-opus-4-6-thinking",
    "claude-sonnet-4.6",
    "gpt-5.3-codex",
    "gpt-5.2-codex",
    "qwen3-max",
    "qwen3-coder-plus",
    "deepseek-ai/DeepSeek-R1",
    "deepseek-ai/DeepSeek-V3.1",
    "gemini-3.1-pro-high",
]

ALIASES = {
    "opus": "claude-opus-4-6-thinking",
    "sonnet": "claude-sonnet-4.6",
    "gpt53": "gpt-5.3-codex",
    "gpt52": "gpt-5.2-codex",
    "qwen": "qwen3-max",
    "r1": "deepseek-ai/DeepSeek-R1",
    "gemini": "gemini-3.1-pro-high",
}

KNOWN_MODELS = {m for group in MODELS.values() for m in group}


def die(msg: str, code: int = 1) -> None:
    print(f"ERROR: {msg}", file=sys.stderr)
    raise SystemExit(code)


def load_config(path: Path) -> dict:
    if not path.exists():
        die(f"OpenClaw config not found: {path}")
    try:
        return json.loads(path.read_text(encoding="utf-8"))
    except json.JSONDecodeError as e:
        die(f"Invalid JSON in {path}: {e}")


def save_config(path: Path, data: dict) -> None:
    path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")


def backup_config(path: Path) -> Path:
    BACKUP_DIR.mkdir(parents=True, exist_ok=True)
    ts = dt.datetime.now().strftime("%Y%m%d_%H%M%S")
    backup = BACKUP_DIR / f"openclaw.json.bak.{ts}"
    shutil.copy2(path, backup)
    return backup


def latest_backup() -> Path | None:
    if not BACKUP_DIR.exists():
        return None
    backups = sorted(BACKUP_DIR.glob("openclaw.json.bak.*"), reverse=True)
    return backups[0] if backups else None


def ensure_schema(cfg: dict) -> None:
    cfg.setdefault("models", {})
    cfg["models"].setdefault("providers", {})
    cfg.setdefault("agents", {})
    cfg["agents"].setdefault("defaults", {})
    cfg["agents"]["defaults"].setdefault("model", {})


def normalize_model_id(model: str) -> str:
    model = model.strip()
    model = model.split("realmrouter/", 1)[1] if model.startswith("realmrouter/") else model
    return ALIASES.get(model.lower(), model)


def provider_models() -> list[dict]:
    return [{"id": m, "name": m} for m in sorted(KNOWN_MODELS)]


def configured_key(cfg: dict) -> str:
    return (
        cfg.get("models", {})
        .get("providers", {})
        .get("realmrouter", {})
        .get("apiKey", "")
        .strip()
    )


def configured_model(cfg: dict) -> str:
    return (
        cfg.get("agents", {})
        .get("defaults", {})
        .get("model", {})
        .get("primary", "realmrouter/qwen3-max")
    )


def resolve_api_key(explicit: str, cfg: dict | None = None) -> str:
    if explicit and explicit.strip():
        return explicit.strip()
    env = os.environ.get(KEY_ENV, "").strip()
    if env:
        return env
    if cfg is not None:
        return configured_key(cfg)
    return ""


def maybe_restart_gateway(should_restart: bool) -> None:
    if not should_restart:
        print("Hint: run 'openclaw gateway restart' to apply changes.")
        return
    try:
        subprocess.run(["openclaw", "gateway", "restart"], check=True)
        print("OK: gateway restarted")
    except Exception as e:
        print(f"WARN: gateway restart failed: {e}")


def test_connectivity(api_key: str, model: str) -> tuple[bool, int, str]:
    payload = {
        "model": model,
        "messages": [{"role": "user", "content": "hi"}],
        "max_tokens": 1,
    }
    req = request.Request(
        f"{API_BASE}/chat/completions",
        data=json.dumps(payload).encode("utf-8"),
        headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
        method="POST",
    )
    try:
        with request.urlopen(req, timeout=20) as resp:
            body = resp.read().decode("utf-8", errors="ignore")
            return True, resp.status, body
    except error.HTTPError as e:
        return False, e.code, e.read().decode("utf-8", errors="ignore")
    except Exception as e:
        return False, 0, str(e)


def verify_key_usable(api_key: str, model: str) -> tuple[bool, int, str]:
    return test_connectivity(api_key, model)


def resolve_model_input(raw: str) -> str:
    raw = raw.strip()
    if raw.isdigit():
        idx = int(raw)
        ordered = model_picker_order()
        if 1 <= idx <= len(ordered):
            return ordered[idx - 1]
        die(f"Model number out of range: {idx}")
    return normalize_model_id(raw)


def model_picker_order() -> list[str]:
    seen = set()
    ordered = []
    for m in PREFERRED + sorted(KNOWN_MODELS):
        if m not in seen:
            seen.add(m)
            ordered.append(m)
    return ordered


def cmd_models_picker(_: argparse.Namespace) -> None:
    ordered = model_picker_order()
    for i, model in enumerate(ordered, 1):
        print(f"[{i}] {model}")


def cmd_install(args: argparse.Namespace) -> None:
    cfg = load_config(args.config)
    [REDACTED_SECRET])
    if not api_key:
        die(f"API key required. Use --api-key or env {KEY_ENV}")

    model = resolve_model_input(args.default_model)

    ok, status, body = verify_key_usable(api_key, model)
    if not ok and not args.force:
        print(f"KEY_VERIFY_FAIL: model={model} http={status}")
        if body:
            print(body[:300])
        die("API key verification failed. Re-run with --force to continue anyway.")

    ensure_schema(cfg)
    backup = backup_config(args.config)

    cfg["models"]["providers"]["realmrouter"] = {
        "baseUrl": API_BASE,
        "apiKey": api_key,
        "api": "openai-completions",
        "models": provider_models(),
    }
    cfg["agents"]["defaults"]["model"]["primary"] = f"realmrouter/{model}"
    save_config(args.config, cfg)

    print(f"OK: installed/updated realmrouter provider, default=realmrouter/{model}")
    print(f"Backup: {backup}")
    if model not in KNOWN_MODELS:
        print(f"WARN: custom model id used: {model}")
    maybe_restart_gateway(args.restart_gateway)


def cmd_update_key(args: argparse.Namespace) -> None:
    cfg = load_config(args.config)
    [REDACTED_SECRET])
    if not api_key:
        die(f"API key required. Use --api-key or env {KEY_ENV}")

    ensure_schema(cfg)
    if "realmrouter" not in cfg["models"]["providers"]:
        die("realmrouter provider not found. Run install first.")

    verify_model = resolve_model_input(args.verify_model) if args.verify_model else normalize_model_id(configured_model(cfg))
    ok, status, body = verify_key_usable(api_key, verify_model)
    if not ok and not args.force:
        print(f"KEY_VERIFY_FAIL: model={verify_model} http={status}")
        if body:
            print(body[:300])
        die("API key verification failed. Re-run with --force to continue anyway.")

    backup = backup_config(args.config)
    cfg["models"]["providers"]["realmrouter"]["apiKey"] = api_key
    save_config(args.config, cfg)
    print("OK: API key updated")
    print(f"Backup: {backup}")
    maybe_restart_gateway(args.restart_gateway)


def cmd_switch_model(args: argparse.Namespace) -> None:
    cfg = load_config(args.config)
    ensure_schema(cfg)

    model = resolve_model_input(args.model)

    key = resolve_api_key(args.api_key, cfg)
    if not key:
        die("No API key found. Set key first before switching model.")

    # Always precheck by default (user request). Use --skip-precheck to bypass.
    if not args.skip_precheck:
        ok, status, body = test_connectivity(key, model)
        if not ok:
            print(f"MODEL_VERIFY_FAIL: model={model} http={status}")
            if body:
                print(body[:300])
            if not args.force:
                die("Model verification failed. Re-run with --force or --skip-precheck to switch anyway.")

    backup = backup_config(args.config)
    cfg["agents"]["defaults"]["model"]["primary"] = f"realmrouter/{model}"
    save_config(args.config, cfg)

    print(f"OK: switched to realmrouter/{model}")
    print(f"Backup: {backup}")
    if model not in KNOWN_MODELS:
        print(f"WARN: custom model id used: {model}")
    maybe_restart_gateway(args.restart_gateway)


def cmd_test(args: argparse.Namespace) -> None:
    cfg = load_config(args.config)
    key = resolve_api_key(args.api_key, cfg)
    if not key:
        die(f"No API key found. Set --api-key or env {KEY_ENV}")

    model = resolve_model_input(args.model) if args.model else normalize_model_id(configured_model(cfg))
    ok, status, body = test_connectivity(key, model)
    print(f"Model: {model}")
    print(f"HTTP: {status}")
    if ok:
        print("OK: connectivity test passed")
        raise SystemExit(0)

    print("FAIL: connectivity test failed")
    if body:
        print(body[:800])
    raise SystemExit(2)


def cmd_show(args: argparse.Namespace) -> None:
    cfg = load_config(args.config)
    provider = "present" if cfg.get("models", {}).get("providers", {}).get("realmrouter") else "missing"
    model = configured_model(cfg)
    key = configured_key(cfg)
    masked = ("*" * max(0, len(key) - 4) + key[-4:]) if key else "(not set)"
    print("realmrouter provider:", provider)
    print("primary model:", model)
    print("api key:", masked)


def cmd_list_models(_: argparse.Namespace) -> None:
    for provider, models in MODELS.items():
        print(f"[{provider}]")
        for m in models:
            print(f"  - {m}")


def cmd_rollback(args: argparse.Namespace) -> None:
    if args.backup:
        backup = Path(args.backup).expanduser().resolve()
    else:
        maybe = latest_backup()
        if not maybe:
            die("No backup found in ~/.openclaw/backups")
        backup = maybe

    if not backup.exists():
        die(f"Backup not found: {backup}")

    current_backup = backup_config(args.config)
    shutil.copy2(backup, args.config)
    print(f"OK: restored from {backup}")
    print(f"Current config backup: {current_backup}")
    maybe_restart_gateway(args.restart_gateway)


def build_parser() -> argparse.ArgumentParser:
    p = argparse.ArgumentParser(description="RealmRouter/OpenClaw config manager")
    p.add_argument("--config", type=Path, default=DEFAULT_CONFIG, help="path to openclaw.json")
    sub = p.add_subparsers(dest="cmd", required=True)

    s1 = sub.add_parser("install", help="inject/refresh provider and set default model")
    s1.add_argument("--api-key", default="", help=f"RealmRouter key (or env {KEY_ENV})")
    s1.add_argument("--default-model", default="qwen3-max", help="model id / alias / picker index")
    s1.add_argument("--force", action="store_true", help="continue even if key verification fails")
    s1.add_argument("--restart-gateway", action="store_true")
    s1.set_defaults(func=cmd_install)

    s2 = sub.add_parser("update-key", help="update API key")
    s2.add_argument("--api-key", default="", help=f"RealmRouter key (or env {KEY_ENV})")
    s2.add_argument("--verify-model", default="", help="verify key using this model (default: current model)")
    s2.add_argument("--force", action="store_true", help="continue even if key verification fails")
    s2.add_argument("--restart-gateway", action="store_true")
    s2.set_defaults(func=cmd_update_key)

    s2b = sub.add_parser("set-key", help="alias of update-key")
    s2b.add_argument("--api-key", default="", help=f"RealmRouter key (or env {KEY_ENV})")
    s2b.add_argument("--verify-model", default="", help="verify key using this model (default: current model)")
    s2b.add_argument("--force", action="store_true", help="continue even if key verification fails")
    s2b.add_argument("--restart-gateway", action="store_true")
    s2b.set_defaults(func=cmd_update_key)

    s3 = sub.add_parser("switch-model", help="switch default model")
    s3.add_argument("--model", required=True, help="model id / alias / picker index")
    s3.add_argument("--api-key", default="", help="optional key override for verification")
    s3.add_argument("--skip-precheck", action="store_true", help="skip model verification before switching")
    s3.add_argument("--force", action="store_true", help="switch even when verification fails")
    s3.add_argument("--restart-gateway", action="store_true")
    s3.set_defaults(func=cmd_switch_model)

    s4 = sub.add_parser("set-model", help="alias of switch-model")
    s4.add_argument("--model", required=True)
    s4.add_argument("--api-key", default="")
    s4.add_argument("--skip-precheck", action="store_true")
    s4.add_argument("--force", action="store_true")
    s4.add_argument("--restart-gateway", action="store_true")
    s4.set_defaults(func=cmd_switch_model)

    s5 = sub.add_parser("test", help="test connectivity")
    s5.add_argument("--api-key", default="", help=f"override key (or env {KEY_ENV})")
    s5.add_argument("--model", default="", help="override model")
    s5.set_defaults(func=cmd_test)

    s6 = sub.add_parser("show", help="show provider/model/key status")
    s6.set_defaults(func=cmd_show)

    s7 = sub.add_parser("list-models", help="print built-in model ids by provider")
    s7.set_defaults(func=cmd_list_models)

    s8 = sub.add_parser("models", help="print numbered model picker list")
    s8.set_defaults(func=cmd_models_picker)

    s9 = sub.add_parser("rollback", help="restore latest backup (or target backup)")
    s9.add_argument("--backup", default="", help="backup file path")
    s9.add_argument("--restart-gateway", action="store_true")
    s9.set_defaults(func=cmd_rollback)

    return p


def main() -> None:
    args = build_parser().parse_args()
    args.func(args)


if __name__ == "__main__":
    main()


path: scripts/rr.sh
content: #!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PY="$SCRIPT_DIR/realm_manager.py"

cmd="${1:-help}"
shift || true

case "$cmd" in
  init)
    model="${1:-qwen3-max}"
    python3 "$PY" install --default-model "$model" --restart-gateway
    ;;
  key|k)
    key="${1:-}"
    if [[ -z "$key" ]]; then
      echo "Usage: rr key <api-key>" >&2
      exit 1
    fi
    python3 "$PY" set-key --api-key "$key" --restart-gateway
    ;;
  use|m)
    model="${1:-}"
    if [[ -z "$model" ]]; then
      echo "Usage: rr use <model-id|alias|index>" >&2
      exit 1
    fi
    python3 "$PY" set-model --model "$model" --restart-gateway
    ;;
  pick)
    python3 "$PY" models
    ;;
  test)
    python3 "$PY" test "$@"
    ;;
  show)
    python3 "$PY" show
    ;;
  list)
    python3 "$PY" list-models
    ;;
  rollback|rb)
    if [[ $# -gt 0 ]]; then
      python3 "$PY" rollback --backup "$1" --restart-gateway
    else
      python3 "$PY" rollback --restart-gateway
    fi
    ;;
  help|--help|-h|*)
    cat <<'EOF'
rr - short wrapper for realmrouter-switch

Commands:
  rr init [model]
  rr key|k <api-key>
  rr use|m <model>
  rr pick
  rr test
  rr show
  rr list
  rr rollback|rb [file]
EOF
    ;;
esac


path: scripts/rr_win.py
content: #!/usr/bin/env python3
import argparse
import subprocess
import sys
from pathlib import Path

SCRIPT_DIR = Path(__file__).resolve().parent
PY = SCRIPT_DIR / "realm_manager.py"


def run(args):
    cmd = [sys.executable, str(PY), *args]
    return subprocess.call(cmd)


def main():
    p = argparse.ArgumentParser()
    p.add_argument("command", nargs="?", default="help")
    p.add_argument("rest", nargs="*")
    ns = p.parse_args()

    c = ns.command.lower()
    r = ns.rest

    if c == "init":
        model = r[0] if r else "qwen3-max"
        raise SystemExit(run(["install", "--default-model", model, "--restart-gateway"]))
    if c in ("key", "k"):
        if not r:
            print("Usage: rr key <api-key>")
            raise SystemExit(1)
        raise SystemExit(run(["set-key", "--api-key", r[0], "--restart-gateway"]))
    if c in ("use", "m"):
        if not r:
            print("Usage: rr use <model-id|alias|index>")
            raise SystemExit(1)
        raise SystemExit(run(["set-model", "--model", r[0], "--restart-gateway"]))
    if c == "pick":
        raise SystemExit(run(["models"]))
    if c == "test":
        raise SystemExit(run(["test", *r]))
    if c == "show":
        raise SystemExit(run(["show"]))
    if c == "list":
        raise SystemExit(run(["list-models"]))
    if c in ("rollback", "rb"):
        if r:
            raise SystemExit(run(["rollback", "--backup", r[0], "--restart-gateway"]))
        raise SystemExit(run(["rollback", "--restart-gateway"]))

    print("""rr (Windows) - short wrapper for realmrouter-switch

Commands:
  rr init [model]
  rr key|k <api-key>
  rr use|m <model>
  rr pick
  rr test
  rr show
  rr list
  rr rollback|rb [file]
""")


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)