#!/usr/bin/env node

/**
 * ============================================================================
 * MyClaw - 学生友好的 OpenClaw 工具
 * ============================================================================
 * 
 * 跨平台要求 / Cross-Platform Requirements:
 * ----------------------------------------
 * 本脚本支持 macOS、Linux 和 Windows。
 * 
 * Windows 支持说明:
 *   - Windows 10+ (Build 1053+) 原生支持 ANSI 颜色代码
 *   - Windows 7/8/10 旧版本: 颜色代码会被 stripped，避免乱码
 *   - 推荐 Windows 用户使用 Windows Terminal 或 PowerShell 7+ 获得最佳体验
 *   - Emoji 符号在 Windows 命令行可能显示为方块，属正常现象
 * 
 * 测试环境:
 *   - macOS: ✅ 完全支持
 *   - Linux: ✅ 完全支持
 *   - Windows 10/11 (PowerShell/CMD): ✅ 基本支持
 * 
 * ============================================================================
 */

const { execSync } = require('child_process');
const os = require('os');
const path = require('path');
const readline = require('readline');
const { createAgent } = require('./create_agent');
const { version } = require(path.join(__dirname, 'package.json'));
const { TOKEN, DEFAULT_URL } = require(path.join(__dirname, 'config'));

const args = process.argv.slice(2);
const command = args[0];

// ============================================================================
// 跨平台检测
// ============================================================================

function detectPlatform() {
  const platform = os.platform();
  if (platform === 'win32') return 'windows';
  if (platform === 'darwin' || platform === 'linux') return 'mac';
  return 'unknown';
}

const isWindows = detectPlatform() === 'windows';

// ============================================================================
// 跨平台颜色输出
// ============================================================================

function makeColors() {
  if (isWindows) {
    return { red: '', green: '', yellow: '', blue: '', cyan: '', white: '', bold: '', dim: '', nc: '' };
  }
  return {
    red: '\x1b[31m',
    green: '\x1b[32m',
    yellow: '\x1b[33m',
    blue: '\x1b[34m',
    cyan: '\x1b[36m',
    white: '\x1b[37m\x1b[1m',
    bold: '\x1b[1m',
    dim: '\x1b[2m',
    nc: '\x1b[0m'
  };
}

const colors = makeColors();

// ============================================================================
// 安装命令
// ============================================================================

function runInstall() {
  const platform = detectPlatform();

  if (platform === 'windows') {
    console.log('[Windows] 检测到 Windows 系统，正在安装...');
    const cmd = 'powershell -c "irm https://openclaw.ai/install.ps1 | iex"';
    console.log('> ' + cmd);
    try {
      execSync(cmd, { stdio: 'inherit' });
    } catch (err) {
      console.error('[错误] 安装失败:', err.message);
      process.exit(1);
    }
  } else if (platform === 'mac') {
    console.log('[macOS/Linux] 检测到系统，正在安装...');
    const cmd = 'curl -fsSL https://openclaw.ai/install.sh | bash';
    console.log('> ' + cmd);
    try {
      execSync(cmd, { stdio: 'inherit' });
    } catch (err) {
      console.error('[错误] 安装失败:', err.message);
      process.exit(1);
    }
  } else {
    console.error('[错误] 不支持的操作系统:', os.platform());
    process.exit(1);
  }
}

// ============================================================================
// 重装命令 (mci)
// ============================================================================

const OPENCLAW_VERSION = '2026.4.5';

function runReinstall() {
  const bar = '────────────────────────────────────────';
  console.log('');
  console.log(bar);
  console.log('  🔄 OpenClaw 重装工具 (锁定版本: ' + colors.green + OPENCLAW_VERSION + colors.nc + ')');
  console.log(bar);
  console.log('');

  // 1. 安装前升级 MyClaw
  console.log('[1/8] ⬆️  升级 MyClaw (安装前)...');
  try {
    runUpdate();
  } catch (err) {
    console.log('      ' + colors.yellow + '⚠ 升级跳过' + colors.nc);
  }
  console.log('');

  // 2. 设置中国镜像
  console.log('[2/8] 🌐 设置 npm 中国镜像...');
  try {
    execSync('npm config set registry https://registry.npmmirror.com', { stdio: 'inherit' });
    console.log('      ' + colors.green + '✓ 已切换到淘宝镜像' + colors.nc);
  } catch (err) {
    console.log('      ' + colors.yellow + '⚠ 镜像设置跳过' + colors.nc);
  }
  console.log('');

  // 3. 清理 npm 缓存
  console.log('[3/8] 🧹 清理 npm 缓存...');
  try {
    execSync('npm cache clean --force', { stdio: 'inherit' });
    console.log('      ' + colors.green + '✓ 缓存清理完成' + colors.nc);
  } catch (err) {
    console.log('      ' + colors.yellow + '⚠ 缓存清理跳过' + colors.nc);
  }
  console.log('');

  // 4. 卸载旧版本
  console.log('[4/8] 🗑️  卸载旧版本...');
  try {
    execSync('npm uninstall -g openclaw', { stdio: 'inherit' });
    console.log('      ' + colors.green + '✓ 卸载完成' + colors.nc);
  } catch (err) {
    console.log('      ' + colors.yellow + '⚠ 卸载跳过（可能未安装）' + colors.nc);
  }
  console.log('');

  // 5. 重新安装特定版本
  console.log('[5/8] 📦 安装 openclaw@' + OPENCLAW_VERSION + '...');
  try {
    execSync('npm install -g openclaw@' + OPENCLAW_VERSION, {
      stdio: 'inherit',
      env: { ...process.env, npm_config_progress: 'true' }
    });
    console.log('      ' + colors.green + '✓ 安装完成' + colors.nc);
  } catch (err) {
    console.error('      ' + colors.red + '✗ 安装失败: ' + err.message + colors.nc);
    process.exit(1);
  }
  console.log('');

  // 6. 安装后升级 MyClaw
  console.log('[6/8] ⬆️  升级 MyClaw (安装后)...');
  try {
    runUpdate();
  } catch (err) {
    console.log('      ' + colors.yellow + '⚠ 升级跳过' + colors.nc);
  }
  console.log('');

  // 7. 验证安装
  console.log('[7/8] ✔️  验证安装...');
  try {
    const version = execSync('openclaw --version', { encoding: 'utf8' }).trim();
    console.log('      ' + colors.green + '✓ ' + version + colors.nc);
  } catch (err) {
    console.error('      ' + colors.red + '✗ 验证失败' + colors.nc);
    process.exit(1);
  }
  console.log('');

  // 8. 启动 Gateway
  console.log('[8/8] 🚀 启动 Gateway...');
  try {
    const start = require('./start');
    start.run();
  } catch (err) {
    console.log('      ' + colors.yellow + '⚠ 启动跳过' + colors.nc);
  }
  console.log('');

  console.log(bar);
  console.log(colors.green + '  ✅ 重装完成！' + colors.nc);
  console.log(bar);
  console.log('');
}

// ============================================================================
// Lock 4.2 — 卸载 openclaw 并锁定安装 2026.4.2
// ============================================================================

function runLock42() {
  const bar = '────────────────────────────────────────';
  const TARGET = '2026.4.2';

  console.log('');
  console.log(bar);
  console.log('  🔒 OpenClaw 版本锁定工具 (目标版本: ' + colors.green + TARGET + colors.nc + ')');
  console.log(bar);
  console.log('');

  // 1. 设置中国镜像
  console.log('[1/3] 🌐 设置 npm 中国镜像...');
  try {
    execSync('npm config set registry https://registry.npmmirror.com', { stdio: 'inherit' });
    const registry = execSync('npm config get registry', { encoding: 'utf8' }).trim();
    console.log('      ' + colors.green + '✓ ' + registry + colors.nc);
  } catch (err) {
    console.log('      ' + colors.yellow + '⚠ 镜像设置跳过' + colors.nc);
  }
  console.log('');

  // 2. 卸载
  console.log('[2/3] 🗑️  卸载当前 openclaw...');
  try {
    execSync('npm uninstall -g openclaw', { stdio: 'inherit' });
    console.log('      ' + colors.green + '✓ 卸载完成' + colors.nc);
  } catch (err) {
    console.log('      ' + colors.yellow + '⚠ 卸载跳过（可能未安装）' + colors.nc);
  }
  console.log('');

  // 3. 安装指定版本
  console.log('[3/3] 📦 安装 openclaw@' + TARGET + '...');
  try {
    execSync('npm install -g openclaw@' + TARGET, { stdio: 'inherit' });
    console.log('      ' + colors.green + '✓ 安装完成' + colors.nc);
  } catch (err) {
    console.error('      ' + colors.red + '✗ 安装失败: ' + err.message + colors.nc);
    process.exit(1);
  }
  console.log('');

  // 验证版本
  try {
    const ver = execSync('openclaw --version', { encoding: 'utf8' }).trim();
    console.log('[验证] ' + colors.green + ver + colors.nc);
  } catch { }

  console.log('');
  console.log(bar);
  console.log(colors.green + '  ✅ 已锁定到 openclaw@' + TARGET + colors.nc);
  console.log(bar);
  console.log('');
}

// ============================================================================
// 状态命令
// ============================================================================

function runStatus() {
  const { findConfig, readConfig } = require('./find-config');
  const configPath = findConfig();
  if (!configPath) {
    console.error('  ❌ 未找到 openclaw.json，请确认 OpenClaw 已安装');
    return;
  }
  const { config } = readConfig();
  const token = config?.gateway?.auth?.token;
  const url = 'http://127.0.0.1:18789' + (token ? '?token=' + token : '');
  console.log('');
  console.log('  ' + colors.green + url + colors.nc);
  console.log('');
  console.log('  ' + colors.dim + '打开方式:' + colors.nc);
  console.log('  ' + colors.dim + '  1. 按住 ' + colors.yellow + 'Control' + colors.dim + ' + 点击链接' + colors.nc);
  console.log('  ' + colors.dim + '  2. 复制链接，在浏览器地址栏打开' + colors.nc);
  console.log('  ' + colors.dim + '  3. ' + colors.yellow + 'mc restart' + colors.dim + ' 后重启自动打开' + colors.nc);
  console.log('');
}

// ============================================================================
// 创建新 Agent
// ============================================================================

function runNew() {
  const G = colors.green;
  const Y = colors.yellow;
  const R = colors.red;
  const B = colors.blue;
  const NC = colors.nc;

  // 支持两种调用：
  //   mc new                              → 交互输入名字
  //   mc new <name>                       → 直接使用传入的名字
  //   mc new <name> --first-message "..." → 自定义首条消息
  const rawName = args[1];
  const firstMessageIdx = args.indexOf('--first-message');
  const customFirstMessage = firstMessageIdx !== -1 ? args[firstMessageIdx + 1] : null;

  if (rawName) {
    doCreate(rawName);
    return;
  }

  // 交互模式：readline 询问名字（校验不通过则重试）
  console.log('');
  if (process.stdin.isTTY) process.stdin.resume();
  askName();

  function askName() {
    const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
    rl.question(B + '请输入 Agent 名字（英文、数字、连字符）: ' + NC, function (answer) {
      rl.close();
      const name = (answer || '').trim();
      if (!name) {
        console.log(R + '名字不能为空，请重新输入。' + NC);
        askName();
        return;
      }
      // 预检：必须包含英文字母或数字
      if (!/[a-zA-Z0-9]/.test(name)) {
        console.log(R + '名字必须包含英文或数字，请重新输入（如 my-agent）。' + NC);
        askName();
        return;
      }
      doCreate(name);
    });
  }

  function doCreate(input) {
    const { normalizeAgentId } = require('./create_agent');

    // 校验 1：中文名 / 非法字符
    var agentId = normalizeAgentId(input);
    if (!agentId || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(agentId)) {
      console.log(R + '名字必须是英文、数字或连字符（如 my-agent），请重新输入。' + NC);
      console.log('');
      if (rawName) { process.exit(1); return; }
      askName();
      return;
    }

    // 校验 2：重名检查（不依赖 createAgent，避免它内部 process.exit）
    const fs = require('fs');
    const path = require('path');
    const os = require('os');
    const configPath = path.join(os.homedir(), '.openclaw', 'openclaw.json');
    try {
      var configData = JSON.parse(fs.readFileSync(configPath, 'utf8'));
      var list = (configData.agents && configData.agents.list) || [];
      if (list.some(function (a) { return a && a.id === agentId; })) {
        console.log(R + 'Agent "' + agentId + '" 已存在，请换一个名字。' + NC);
        console.log('');
        if (rawName) { process.exit(1); return; }
        askName();
        return;
      }
    } catch (e) {
      // 配置文件读不到就算了，交给 createAgent 处理
    }

    // 校验通过，开始创建
    console.log(Y + '正在创建 Agent "' + agentId + '"...' + NC);
    console.log('');

    var result;
    try {
      const createOpts = customFirstMessage ? { firstMessage: customFirstMessage } : {};
      result = require('./create_agent').createAgent(input, createOpts);
    } catch (err) {
      console.log(R + '创建失败: ' + err.message + NC);
      process.exit(1);
    }

    // 打印结果
    console.log(G + '✅' + NC + ' Agent "' + agentId + '" 创建完成');
    console.log('');
    console.log('  ' + G + '✅' + NC + ' 名称标准化    ' + agentId);
    console.log('  ' + G + '✅' + NC + ' 环境检查       无冲突');
    console.log('  ' + G + '✅' + NC + ' 创建工作空间   已就绪');
    console.log('  ' + G + '✅' + NC + ' 注册配置       已写入');
    console.log('  ' + Y + '⏳' + NC + ' 唤醒 Agent     发送中...');
    console.log('');
    console.log('下一步: 进入龙虾，选择最上面的助手，去和她对话吧~');
    console.log('');
    console.log('  ' + colors.dim + 'http://127.0.0.1:18789?token=' + TOKEN + NC);
    console.log('');
    console.log(colors.dim + '如果唤醒失败，可手动发送命令:' + NC);
    console.log('  ' + Y + 'openclaw agent --agent ' + agentId + ' --message "你好"' + NC);
    console.log('');

    // 在桌面创建/覆盖浏览器快捷图标
    createDesktopShortcut('http://127.0.0.1:18789?token=' + TOKEN);

    process.exit(0);
  }
}

// ============================================================================
// 向导系统（统一入口）
// ============================================================================

function runWeixin() {
  const { runWeixin, runRebind: _rb } = require('./wizards/index');
  runWeixin();
}
function runRebind() {
  const { runRebind } = require('./wizards/index');
  runRebind();
}

// ============================================================================
// Fix 兜底修复 (独立模块)
// ============================================================================

function runFix() {
  const fix = require('./fix');
  fix.run();
}

// ============================================================================
// WSL2 安装 (独立模块)
// ============================================================================

function runWsl2() {
  const wsl2 = require('./wsl2');
  wsl2.run();
}

function runRestrict() {
  const restrict = require('./restrict');
  restrict.run();
}

async function runLaunch() {
  const launch = require('./launch');
  await launch.run();
}

async function runStart() {
  const start = require('./start');
  await start.run();
}

// ============================================================================
// Prepare 智能初始化
// ============================================================================

function runPrepare() {
  const prepare = require('./prepare');
  prepare.run().catch((err) => {
    console.error('[错误] ' + err.message);
    process.exit(1);
  });
}

// ============================================================================
// TUI 唤起新对话上下文
// ============================================================================

// 预置的特殊 session ID 字典（按顺序使用）
const TUI_SESSION_SUFFIXES = [
  'helper', 'AAA', 'GO', '007', 'VIP', '666',
  'China', 'PRO', 'MAX', 'TOP', 'BEST',
  'GO', 'OK', 'YES', 'WIN', 'FUN'
];

// Session 索引存储文件
const SESSION_INDEX_FILE = path.join(os.homedir(), '.openclaw', 'myclaw-session-index.json');

/**
 * 获取 agent 的下一个 session 序号索引
 */
function getNextSessionIndex(agentName) {
  const fs = require('fs');
  let indexData = {};

  // 读取现有索引
  if (fs.existsSync(SESSION_INDEX_FILE)) {
    try {
      const raw = fs.readFileSync(SESSION_INDEX_FILE, 'utf8');
      indexData = JSON.parse(raw);
    } catch (err) {
      console.warn('[' + colors.yellow + '警告' + colors.nc + '] 读取 session 索引失败，将重置: ' + err.message);
    }
  }

  // 获取当前 agent 的索引，不存在则从 0 开始
  const currentIndex = indexData[agentName] || 0;

  // 计算下一个索引（循环使用）
  const nextIndex = currentIndex % TUI_SESSION_SUFFIXES.length;

  // 更新索引数据
  indexData[agentName] = currentIndex + 1;

  // 确保目录存在
  const dir = path.dirname(SESSION_INDEX_FILE);
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
  }

  // 写回文件
  try {
    fs.writeFileSync(SESSION_INDEX_FILE, JSON.stringify(indexData, null, 2) + '\n', 'utf8');
  } catch (err) {
    console.warn('[' + colors.yellow + '警告' + colors.nc + '] 保存 session 索引失败: ' + err.message);
  }

  return nextIndex;
}

/**
 * 获取 agent 的下一个 session 后缀
 */
function getNextSessionSuffix(agentName) {
  const index = getNextSessionIndex(agentName);
  return TUI_SESSION_SUFFIXES[index];
}

function runTui(interactive = false) {
  const { spawn } = require('child_process');

  // 解析 --session 参数（支持完整 session key 或仅后缀）
  const sessionIdx = args.indexOf('--session');
  const customSession = sessionIdx !== -1 ? args[sessionIdx + 1] : null;

  // 获取 agent 名称
  let agentName;
  if (interactive) {
    // 交互模式：需要询问用户
    return askAgentNameForTui(customSession);
  } else {
    // 命令行模式：从参数获取，默认 'main'
    // 如果 args[1] 是 --session 本身，不当作 agentName
    agentName = (args[1] && args[1] !== '--session') ? args[1] : 'main';
    doTuiCommand(agentName, customSession);
  }
}

// 交互式询问 Agent 名称
function askAgentNameForTui(customSession) {
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
  rl.question(colors.cyan + '请输入 Agent 名称: ' + colors.nc, function (answer) {
    rl.close();
    const agentName = (answer || '').trim();
    if (!agentName) {
      console.log(colors.red + 'Agent 名称不能为空。' + colors.nc);
      console.log('');
      askAgentNameForTui(customSession);
      return;
    }
    doTuiCommand(agentName, customSession);
  });
}

// 执行 TUI 命令
function doTuiCommand(agentName, customSession) {
  const { spawn } = require('child_process');

  // 计算 sessionKey：
  // 1. 传入完整 key（含冒号）→ 直接使用
  // 2. 传入仅后缀 → 拼接为 agent:<agentName>:<suffix>
  // 3. 未传入 → 使用预设轮询后缀
  let sessionKey;
  if (customSession) {
    sessionKey = customSession.includes(':') ? customSession : 'agent:' + agentName + ':' + customSession;
  } else {
    const sessionSuffix = getNextSessionSuffix(agentName);
    sessionKey = 'agent:' + agentName + ':' + sessionSuffix;
  }

  // 解析 --message 参数，默认 '你好'
  const messageIdx = args.indexOf('--message');
  const hasCustomMessage = messageIdx !== -1 && args[messageIdx + 1];
  const messageParam = hasCustomMessage ? args[messageIdx + 1] : '你好';

  // --thinking：默认 adaptive，支持手动覆盖
  const thinkingIdx = args.indexOf('--thinking');
  const thinkingParam = thinkingIdx !== -1 ? args[thinkingIdx + 1] : 'adaptive';

  console.log('[' + colors.blue + 'TUI' + colors.nc + '] 唤起新对话上下文');
  console.log('  Agent: ' + colors.green + agentName + colors.nc);
  console.log('  Session: ' + colors.cyan + sessionKey + colors.nc);
  console.log('  Message: ' + colors.dim + messageParam + colors.nc);
  console.log('  Thinking: ' + colors.dim + thinkingParam + colors.nc);
  console.log('');

  // 发送消息给 agent
  const openclawArgs = ['tui', '--session', sessionKey, '--message', messageParam, '--thinking', thinkingParam];

  // 打印命令时给消息加上引号（仅用于显示）
  const displayArgs = ['tui', '--session', sessionKey, '--message', '"' + messageParam + '"', '--thinking', thinkingParam];
  console.log('[' + colors.dim + '执行命令: openclaw ' + displayArgs.join(' ') + colors.nc + ']');
  console.log('');

  // 启动进程，保存引用以便稍后杀掉
  const child = spawn('openclaw', openclawArgs, {
    stdio: 'ignore'
  });

  console.log('[' + colors.blue + 'TUI' + colors.nc + '] 消息已发送，等待对话生成...');

  // 等待 15 秒后杀掉进程
  setTimeout(() => {
    child.kill();
    console.log('[' + colors.green + '完成' + colors.nc + '] 对话上下文已唤起');
    process.exit(0);
  }, 15000);
}

// ============================================================================
// mc test — 提示词压力测试
// ============================================================================

const TEST_PROMPTS = {
  t1: '请生成一段严谨的、不少于1000字的魔兽世界故事。要求：情节完整，人物鲜明，世界观设定准确，语言流畅正式。',
  t2: '请搜索AI领域的最新新闻，分两部分列出：第一部分是今天的AI新闻，列出最重要的5条；第二部分是本月的AI新闻，列出最重要的5条。每条注明来源和简短说明。',
  t3: '现在是5月，请先用优美的文字描述中国此季节最美丽的一个地方（包括地名、景色、氛围），然后根据这段描述生成一张对应的风景图片。',
};

function runTest() {
  const testKey = args[1]; // e.g. 't1', '1', 't2', '2'

  // 支持 t1/1, t2/2 等格式
  const normalizedKey = testKey ? testKey.replace(/^(\d+)$/, 't$1') : null;

  if (!normalizedKey || !TEST_PROMPTS[normalizedKey]) {
    const available = Object.keys(TEST_PROMPTS).join(', ');
    console.error(colors.red + '[mc test] 用法: mc test <编号>' + colors.nc);
    console.log('  可用编号: ' + colors.cyan + available + colors.nc);
    Object.entries(TEST_PROMPTS).forEach(([key, prompt]) => {
      console.log('  ' + colors.yellow + key + colors.nc + ' — ' + prompt.slice(0, 40) + '...');
    });
    process.exit(1);
  }

  const message = TEST_PROMPTS[normalizedKey];
  const agentName = 'stress-test';
  const sessionKey = 'agent:' + agentName + ':dev';

  console.log('[' + colors.blue + 'TEST' + colors.nc + '] 压力测试: ' + colors.yellow + normalizedKey + colors.nc);
  console.log('  Session: ' + colors.cyan + sessionKey + colors.nc);
  console.log('  Prompt:  ' + colors.dim + message.slice(0, 60) + '...' + colors.nc);
  console.log('');

  const openclawArgs = ['tui', '--session', sessionKey, '--message', message, '--thinking', 'adaptive'];
  const displayArgs = ['tui', '--session', sessionKey, '--message', '"' + message.slice(0, 30) + '..."', '--thinking', 'adaptive'];
  console.log('[' + colors.dim + '执行命令: openclaw ' + displayArgs.join(' ') + colors.nc + ']');
  console.log('');

  const { spawn } = require('child_process');
  const child = spawn('openclaw', openclawArgs, { stdio: 'ignore' });

  console.log('[' + colors.blue + 'TEST' + colors.nc + '] 消息已发送，等待对话生成...');
  setTimeout(() => {
    child.kill();
    console.log('[' + colors.green + '完成' + colors.nc + '] stress-test 对话已唤起');
    process.exit(0);
  }, 5000);
}

// ============================================================================
// 创建 Windows 桌面快捷启动脚本
// ============================================================================

function runBat() {
  const os = require('os');
  const path = require('path');
  const fs = require('fs');

  if (os.platform() !== 'win32') {
    console.error('[' + colors.yellow + '提示' + colors.nc + '] 本命令仅用于 Windows 系统。');
    process.exit(0);
  }

  const myClawDir = path.join(process.env.LOCALAPPDATA || os.tmpdir(), 'myclaw');
  try { fs.mkdirSync(myClawDir, { recursive: true }); } catch { }

  const batPath = path.join(myClawDir, 'openclaw-launcher.bat');
  const desktopPath = path.join(os.homedir(), 'Desktop');

  // 生成时间戳
  const now = new Date();
  const mm = String(now.getMonth() + 1).padStart(2, '0');
  const dd = String(now.getDate()).padStart(2, '0');
  const hh = String(now.getHours()).padStart(2, '0');
  const mi = String(now.getMinutes()).padStart(2, '0');
  const ss = String(now.getSeconds()).padStart(2, '0');
  const ver = require('./package.json').version;

  // 删除旧的 OpenClaw*.lnk
  try {
    const oldLinks = fs.readdirSync(desktopPath).filter(f => f.endsWith('.lnk') && (f.includes('OpenClaw') || f.match(/^\d+\.\d+\.\d+_OpenClaw/)));
    for (const old of oldLinks) {
      try { fs.unlinkSync(path.join(desktopPath, old)); } catch { }
    }
  } catch { }

  const lnkPath = path.join(desktopPath, ver + '_OpenClaw_' + mm + '-' + dd + '_' + hh + '-' + mi + '-' + ss + '.lnk');

  // === 智能启动器 bat 内容 ===
  // 首次启动: myclaw start 会阻塞在当前窗口（Gateway 运行期间不退出）
  // 非首次启动: myclaw start 跑完后 pause 防止闪退
  const batContent = `@echo off
chcp 65001 >nul 2>&1
title OpenClaw
echo.
echo  ============================================
echo   OpenClaw
echo  ============================================
echo.
myclaw start
echo.
echo  按任意键关闭此窗口...
pause >nul
`;



  // 写入 bat 文件（Windows CMD 需要 CRLF 换行符）
  fs.writeFileSync(batPath, batContent.replace(/\n/g, '\r\n'), 'utf8');

  // 用 PowerShell 创建带图标的桌面快捷方式 + 刷新桌面
  // 用 PowerShell 下载图标并创建带图标的桌面快捷方式 + 刷新桌面
  const iconPath = path.join(myClawDir, 'openclaw.ico');
  const iconUrl = 'https://cdn.yiranlaoshi.com/software/myclaw/openclaw.ico';

  const psFile = path.join(myClawDir, 'create-shortcuts.ps1');
  const psContent = [
    '$ErrorActionPreference = \'Continue\'',
    '',
    '# 下载图标（如不存在）',
    'if (-not (Test-Path \'' + iconPath + '\')) {',
    '    try { Invoke-WebRequest -Uri \'' + iconUrl + '\' -OutFile \'' + iconPath + '\' -UseBasicParsing } catch {}',
    '}',
    '',
    '# 创建 OpenClaw 启动快捷方式',
    '$ws = New-Object -ComObject WScript.Shell',
    '$sc = $ws.CreateShortcut(\'' + lnkPath + '\')',
    '$sc.TargetPath = \'' + batPath + '\'',
    '$sc.WorkingDirectory = \'' + myClawDir + '\'',
    '$sc.WindowStyle = 1',
    '$sc.IconLocation = \'' + iconPath + '\'',
    '$sc.Description = \'OpenClaw\'',
    '$sc.Save()',
    'Write-Host \'[bat] OpenClaw shortcut created\'',
    '',
    '# 创建「我的私人龙虾」工作目录快捷方式（如不存在）',
    '$workspaceLnk = \'' + desktopPath + '\\我的私人龙虾.lnk\'',
    'if (-not (Test-Path $workspaceLnk)) {',
    '    $ws2 = New-Object -ComObject WScript.Shell',
    '    $sc2 = $ws2.CreateShortcut($workspaceLnk)',
    '    $sc2.TargetPath = "$env:WINDIR\\explorer.exe"',
    '    $sc2.Arguments = \'\\\\wsl.localhost\\OpenClaw\\root\\.openclaw\\workspace\'',
    '    $sc2.WorkingDirectory = $env:USERPROFILE',
    '    $sc2.Description = \'OpenClaw Workspace\'',
    '    $sc2.IconLocation = "$env:WINDIR\\System32\\imageres.dll,137"',
    '    $sc2.Save()',
    '    Write-Host \'[bat] workspace shortcut created\'',
    '} else {',
    '    Write-Host \'[bat] workspace shortcut already exists\'',
    '}',
    '',
    '# 刷新桌面',
    'try {',
    '    Add-Type -TypeDefinition \'using System; using System.Runtime.InteropServices; public class ShellRefresh { [DllImport(\"shell32.dll\")] public static extern void SHChangeNotify(int e, int f, IntPtr i1, IntPtr i2); }\' -ErrorAction Stop',
    '    [ShellRefresh]::SHChangeNotify(0x08000000, 0, [IntPtr]::Zero, [IntPtr]::Zero)',
    '    Write-Host \'[bat] desktop refreshed\'',
    '} catch {',
    '    Write-Host \'[bat] refresh skipped\'',
    '}',
  ].join('\r\n');

  // UTF-8 BOM + 写入
  fs.writeFileSync(psFile, '\uFEFF' + psContent, 'utf8');

  try {
    const { execSync } = require('child_process');
    const psOutput = execSync('powershell -NoProfile -ExecutionPolicy Bypass -File "' + psFile + '"', {
      encoding: 'utf8',
      stdio: ['pipe', 'pipe', 'pipe'],
    });
    // 打印 PowerShell 的详细日志
    if (psOutput) {
      psOutput.trim().split('\n').forEach(line => {
        if (line.trim()) console.log('  ' + line.trim());
      });
    }

    console.log('');
    console.log('[' + colors.green + '成功' + colors.nc + '] 桌面快捷方式已创建！');
    console.log('');
    console.log('  快捷方式: ' + colors.yellow + lnkPath + colors.nc);
    console.log('  脚本位置: ' + colors.yellow + batPath + colors.nc);
    console.log('');
    console.log('双击桌面上的 ' + colors.yellow + 'OpenClaw' + colors.nc + ' 图标即可智能启动：');
    console.log('  • WSL 未装 → 自动安装');
    console.log('  • 环境未导入 → 自动导入');
    console.log('  • 服务未启动 → 自动启动 + 打开浏览器');
    console.log('  • 已在运行 → 直接打开浏览器');
    console.log('');
  } catch (err) {
    // 如果创建快捷方式失败，至少 bat 已经生成了
    console.log('');
    console.log('[' + colors.yellow + '提示' + colors.nc + '] 快捷方式创建失败，但启动脚本已生成');
    console.log('  脚本位置: ' + colors.yellow + batPath + colors.nc);
    console.log('  你可以手动将此文件拖到桌面');
    console.log('');
  }
}

// ============================================================================
// Open 命令 - 打开浏览器（自动带 token）
// ============================================================================

function runOpen() {
  const fs = require('fs');
  const { exec } = require('child_process');
  const baseUrl = args[1] || DEFAULT_URL;
  const platform = os.platform();
  const bar = '----------------------------------------';

  // 拼接 token
  const url = baseUrl + (baseUrl.includes('?') ? '&' : '?') + 'token=' + TOKEN;

  console.log('');
  console.log('[' + colors.blue + 'MyClaw' + colors.nc + '] ' + colors.blue + '打开浏览器' + colors.nc);
  console.log(bar);
  console.log('');
  console.log('[Token] ' + TOKEN);
  console.log('[地址] ' + url);
  console.log('');

  // 定义各平台 Chrome 路径
  let chromePaths = [];
  if (platform === 'darwin') {
    chromePaths = [
      '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
    ];
  } else if (platform === 'linux') {
    // 检测是否在 WSL2 环境下
    let isWSL = false;
    try {
      const releaseInfo = fs.readFileSync('/proc/version', 'utf8');
      if (/microsoft|wsl/i.test(releaseInfo)) isWSL = true;
    } catch { }

    if (isWSL) {
      // WSL2 环境下优先使用 Windows 端的 Chrome
      chromePaths = [
        '/mnt/c/Program Files/Google/Chrome/Application/chrome.exe',
        '/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe',
      ];
    } else {
      chromePaths = [
        '/usr/bin/google-chrome',
        '/usr/bin/google-chrome-stable',
        '/usr/bin/chromium-browser',
        '/usr/bin/chromium',
      ];
    }
  } else if (platform === 'win32') {
    chromePaths = [
      path.join(process.env.PROGRAMFILES || 'C:\\Program Files', 'Google', 'Chrome', 'Application', 'chrome.exe'),
      path.join(process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)', 'Google', 'Chrome', 'Application', 'chrome.exe'),
      path.join(os.homedir(), 'AppData', 'Local', 'Google', 'Chrome', 'Application', 'chrome.exe'),
    ];
  }

  // 查找 Chrome
  let chromePath = null;
  for (const p of chromePaths) {
    try {
      if (fs.existsSync(p)) {
        chromePath = p;
        break;
      }
    } catch { }
  }

  // 如果没有找到 Chrome
  if (!chromePath) {
    console.log('[' + colors.yellow + '提示' + colors.nc + '] 未检测到 Chrome 浏览器');
    console.log('');
    console.log('正在打开 Chrome 下载页面...');
    console.log('');

    const downloadUrl = 'https://www.google.com/chrome/';
    try {
      if (platform === 'darwin') {
        execSync('open "' + downloadUrl + '"', { stdio: 'ignore' });
      } else if (platform === 'win32') {
        execSync('start "" "' + downloadUrl + '"', { stdio: 'ignore', shell: true });
      } else {
        // Linux / WSL2
        try {
          execSync('xdg-open "' + downloadUrl + '"', { stdio: 'ignore' });
        } catch {
          // WSL2 fallback
          try {
            execSync('powershell.exe -Command "Start-Process \'' + downloadUrl + '\'"', { stdio: 'ignore' });
          } catch {
            console.log('请手动下载 Chrome: ' + colors.yellow + downloadUrl + colors.nc);
          }
        }
      }
    } catch { }

    console.log('安装 Chrome 后重新运行: ' + colors.yellow + 'myclaw open' + colors.nc);
    console.log('');
    console.log(bar);
    console.log('');
    return;
  }

  // 打开 URL
  console.log('[Chrome] ' + colors.green + '已检测到' + colors.nc);
  console.log('  路径: ' + chromePath);
  console.log('');
  console.log('[打开] ' + url);

  try {
    if (platform === 'darwin') {
      // macOS: 使用 open 命令打开 Chrome
      exec('open -a "Google Chrome" "' + url + '"');
    } else if (platform === 'win32') {
      exec('"' + chromePath + '" "' + url + '"');
    } else {
      // Linux / WSL2
      exec('"' + chromePath + '" "' + url + '"');
    }
    console.log('');
    console.log('[' + colors.green + '成功' + colors.nc + '] 浏览器已启动');
  } catch (err) {
    console.error('[' + colors.red + '错误' + colors.nc + '] 打开浏览器失败: ' + err.message);
  }

  console.log('');
  console.log(bar);
  console.log('');
}

// ============================================================================
// 桌面快捷方式（浏览器入口图标）
// ============================================================================

function createDesktopShortcut(url) {
  const fs = require('fs');
  const platform = os.platform();

  // 查找桌面路径（Windows 兼容 OneDrive 同步桌面）
  function findDesktop() {
    const candidates = [path.join(os.homedir(), 'Desktop')];
    if (platform === 'win32') {
      candidates.push(
        path.join(os.homedir(), 'OneDrive', 'Desktop'),
        path.join(os.homedir(), 'OneDrive - Personal', 'Desktop'),
        path.join(os.homedir(), 'OneDrive - 个人', 'Desktop'),
      );
    }
    for (const d of candidates) {
      try { if (fs.existsSync(d)) return d; } catch { }
    }
    return null;
  }

  const desktopPath = findDesktop();
  if (!desktopPath) {
    console.log('[shortcut] ⚠ 未找到桌面目录，跳过');
    return;
  }

  if (platform === 'darwin') {
    // macOS: .webloc 文件，双击用默认浏览器打开
    const webloc = path.join(desktopPath, 'OpenClaw.webloc');
    const content = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>URL</key>
  <string>${url}</string>
</dict>
</plist>`;
    fs.writeFileSync(webloc, content, 'utf8');
    console.log('[shortcut] ✅ 桌面快捷方式已更新: OpenClaw.webloc');
  } else if (platform === 'linux') {
    // Linux / WSL: .desktop 文件
    const desktop = path.join(desktopPath, 'OpenClaw.desktop');
    const content = `[Desktop Entry]
Type=Link
Name=OpenClaw
URL=${url}
Icon=text-html
`;
    fs.writeFileSync(desktop, content, 'utf8');
    try { execSync('chmod +x "' + desktop + '"', { stdio: 'pipe' }); } catch { }
    console.log('[shortcut] ✅ 桌面快捷方式已更新: OpenClaw.desktop');
  } else if (platform === 'win32') {
    // Windows: .url 文件（Internet Shortcut），Windows 原生支持，双击在默认浏览器打开
    // 无需 PowerShell，直接写文件，100% 可靠
    const urlFile = path.join(desktopPath, '打开网页小龙虾.url');
    const content = '[InternetShortcut]\r\nURL=' + url + '\r\n';
    try {
      fs.writeFileSync(urlFile, content, 'utf8');
      console.log('[shortcut] ✅ 桌面快捷方式已创建: 打开网页小龙虾.url');
      console.log('[shortcut]   路径: ' + urlFile);
    } catch (err) {
      console.log('[shortcut] ⚠ 创建失败: ' + err.message);
    }
  }
}

// ============================================================================
// Patch / Unpatch / Restart
// ============================================================================

function runPatch() {
  const { patch, status: patchStatus } = require('./patches/patch');
  const { patchSkills } = require('./patches/patch-skill');
  const { patchConfig } = require('./find-config');
  const bar = '----------------------------------------';

  console.log('');
  console.log('[' + colors.blue + 'MyClaw' + colors.nc + '] ' + colors.blue + 'Patch (UI + Skills + Config)' + colors.nc);
  console.log(bar);
  console.log('');

  // 1. UI 注入
  const uiResult = patch();

  // 2. Skill 注入
  console.log('');
  const skillResult = patchSkills();

  // 3. Python 依赖安装（读取 requirements.txt，pip install 缺失的包）
  console.log('');
  const { patchPythonDeps } = require('./patches/patch-python-deps');
  patchPythonDeps();

  // 4. 智能体上传（从 agent-list/ 复制到 ~/.openclaw/）
  console.log('');
  const { patchAgents } = require('./patches/patch-agent');
  const agentResult = patchAgents();

  // 5. Config 补丁（从 manifest 读取，修改 openclaw.json）
  console.log('');
  try {
    const { loadManifest } = require('./patches/patch-agent');
    const manifest = loadManifest();
    if (manifest && manifest.config && manifest.config.patches) {
      const configStrategy = manifest.config.strategy || 'on';
      if (configStrategy !== 'off') {
        // 将 dot-notation patch 展开为嵌套对象
        const patches = manifest.config.patches;
        const nested = {};
        for (const [dotKey, value] of Object.entries(patches)) {
          const parts = dotKey.split('.');
          let current = nested;
          for (let i = 0; i < parts.length - 1; i++) {
            if (!current[parts[i]] || typeof current[parts[i]] !== 'object') {
              current[parts[i]] = {};
            }
            current = current[parts[i]];
          }
          current[parts[parts.length - 1]] = value;
        }
        const { config, configPath } = patchConfig(nested);

        // 【关键修复】深度合并不会删除错误的历史残留配置，这里主动帮学生清理掉遗留在顶层的 "exec"
        let requireSave = false;
        if (config && config.exec) {
          delete config.exec;
          requireSave = true;
          console.log('[myclaw-config] ✅ 已清理历史遗留的错误顶层 "exec" 常驻配置');
        }

        if (requireSave) {
          const { writeConfig } = require('./find-config');
          writeConfig(config, configPath);
        }

        for (const [key, value] of Object.entries(patches)) {
          console.log('[myclaw-config] ✅ ' + key + ' → ' + JSON.stringify(value));
        }
      } else {
        console.log('[myclaw-config] ⊘ Config 补丁已禁用 [disabled]');
      }
    }
  } catch (err) {
    console.log('[myclaw-config] ⚠ 配置补丁失败: ' + err.message);
  }

  // 6. 自定义注入脚本执行引擎 (Manifest `run` 数组)
  // 此处读取 manifest.json 中的 "run" 数组，并按顺序 require 执行对应的模块。
  // 通过修改 manifest 即可控制额外脚本（如 inject-image, inject-search）是否跟随 patch 自动执行，
  // 从而避免硬编码具体要注入哪些模块。
  try {
    const { loadManifest } = require('./patches/patch-agent');
    const m = loadManifest();
    if (m && Array.isArray(m.run) && m.run.length > 0) {
      console.log('');
      for (const entry of m.run) {
        const strategy = entry.strategy || 'auto';
        const desc = entry.description || entry.module;
        if (strategy === 'off') {
          console.log('[myclaw-run] ⊘ 跳过: ' + desc + ' [off]');
          continue;
        }
        try {
          console.log('[myclaw-run] → 执行: ' + desc);
          const mod = require(entry.module);
          mod.run([]);
          console.log('[myclaw-run] ✅ 完成: ' + desc);
        } catch (err) {
          console.log('[myclaw-run] ❌ 失败: ' + desc + ' — ' + err.message);
        }
      }
    }
  } catch (err) {
    // manifest 加载失败，跳过
  }

  if (uiResult.success || skillResult.success || (agentResult && agentResult.success)) {
    console.log('');
    console.log(bar);
    console.log('下一步: 运行 ' + colors.yellow + 'myclaw restart' + colors.nc + ' 重启 Gateway 使注入生效');
    console.log('');
  }
}

function runNuke(cliArgs) {
  const bar = '════════════════════════════════════════';
  const hasDangerous = (cliArgs || []).includes('--dangerous');

  if (!hasDangerous) {
    console.log('');
    console.log(colors.red + bar + colors.nc);
    console.log(colors.red + '  ⚠️  危险操作：mc nuke' + colors.nc);
    console.log(colors.red + bar + colors.nc);
    console.log('');
    console.log('  此命令将执行以下操作：');
    console.log('  1. ' + colors.red + 'rm -rf /root/.openclaw' + colors.nc);
    console.log('  2. ' + colors.red + 'rm -rf ~/.openclaw' + colors.nc);
    console.log('  3. openclaw onboard --non-interactive --accept-risk --skip-health');
    console.log('  4. mc up');
    console.log('  5. mc patch');
    console.log('  6. mc restart');
    console.log('');
    console.log('  必须加上 ' + colors.yellow + '--dangerous' + colors.nc + ' 参数才会真正执行：');
    console.log('');
    console.log('  ' + colors.yellow + 'mc nuke --dangerous' + colors.nc);
    console.log('');
    console.log(colors.red + bar + colors.nc);
    console.log('');
    return;
  }

  console.log('');
  console.log(colors.red + bar + colors.nc);
  console.log(colors.red + '  💣 mc nuke --dangerous 开始执行' + colors.nc);
  console.log(colors.red + bar + colors.nc);
  console.log('');

  // Step 1: rm -rf /root/.openclaw
  console.log('[1/6] ' + colors.red + 'rm -rf /root/.openclaw' + colors.nc);
  try {
    execSync('rm -rf /root/.openclaw', { stdio: 'inherit' });
    console.log('      ' + colors.green + '✓ 完成' + colors.nc);
  } catch (err) {
    console.log('      ' + colors.yellow + '⚠ 跳过: ' + err.message + colors.nc);
  }
  console.log('');

  // Step 2: rm -rf ~/.openclaw
  console.log('[2/6] ' + colors.red + 'rm -rf ~/.openclaw' + colors.nc);
  try {
    execSync('rm -rf ' + os.homedir() + '/.openclaw', { stdio: 'inherit' });
    console.log('      ' + colors.green + '✓ 完成' + colors.nc);
  } catch (err) {
    console.log('      ' + colors.red + '✗ 失败: ' + err.message + colors.nc);
    process.exit(1);
  }
  console.log('');

  // Step 3: openclaw onboard
  console.log('[3/6] openclaw onboard --non-interactive --accept-risk --skip-health');
  try {
    execSync('openclaw onboard --non-interactive --accept-risk --skip-health', { stdio: 'inherit' });
    console.log('      ' + colors.green + '✓ 完成' + colors.nc);
  } catch (err) {
    console.log('      ' + colors.red + '✗ 失败: ' + err.message + colors.nc);
    process.exit(1);
  }
  console.log('');

  // Step 4: mc up
  console.log('[4/6] mc up (upgrade + patch)');
  try {
    runUpdate();
    console.log('      ' + colors.green + '✓ 升级完成' + colors.nc);
  } catch (err) {
    console.log('      ' + colors.yellow + '⚠ 升级跳过: ' + err.message + colors.nc);
  }
  console.log('');

  // Step 5: mc patch
  console.log('[5/6] mc patch');
  try {
    runPatch();
    console.log('      ' + colors.green + '✓ 完成' + colors.nc);
  } catch (err) {
    console.log('      ' + colors.yellow + '⚠ patch 失败: ' + err.message + colors.nc);
  }
  console.log('');

  // Step 6: mc restart
  console.log('[6/6] mc restart');
  try {
    runRestart();
  } catch (err) {
    console.log('      ' + colors.yellow + '⚠ 重启失败: ' + err.message + colors.nc);
  }

  console.log('');
  console.log(colors.green + bar + colors.nc);
  console.log(colors.green + '  ✅ nuke 完成！' + colors.nc);
  console.log(colors.green + bar + colors.nc);
  console.log('');
}

function runUnpatch() {
  const { unpatch } = require('./patches/patch');
  const bar = '----------------------------------------';

  console.log('');
  console.log('[' + colors.blue + 'MyClaw' + colors.nc + '] ' + colors.blue + 'UI 回滚' + colors.nc);
  console.log(bar);
  console.log('');

  unpatch();

  console.log('');
  console.log(bar);
  console.log('下一步: 运行 ' + colors.yellow + 'myclaw restart' + colors.nc + ' 重启 Gateway');
  console.log('');
}

function runRestart() {
  const gateway = require('./gateway');
  const bar = '----------------------------------------';

  console.log('');
  console.log('[' + colors.blue + 'MyClaw' + colors.nc + '] ' + colors.blue + '重启 Gateway' + colors.nc);
  console.log(bar);
  console.log('');

  try {
    gateway.restart();
  } catch (err) {
    console.error('[' + colors.red + '错误' + colors.nc + '] 重启失败: ' + err.message);
  }

  console.log('');
  console.log(bar);
  console.log('');
}

// ============================================================================
// 卸载命令
// ============================================================================

function runUninstall() {
  const bar = '----------------------------------------';

  console.log('');
  console.log('[' + colors.yellow + 'MyClaw 卸载' + colors.nc + ']');
  console.log(bar);
  console.log('');
  console.log('  ' + colors.yellow + '⚠️  即将执行以下操作：' + colors.nc);
  console.log('  • 将 npm 仓库地址恢复为官方源');
  console.log('  • ' + colors.dim + '（不会删除本程序文件）' + colors.nc);
  console.log('');

  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });

  rl.question('  确认卸载？' + colors.cyan + '(y/n)' + colors.nc + ': ', (answer) => {
    rl.close();

    if (answer.toLowerCase() === 'y') {
      console.log('');
      console.log('  ' + colors.blue + '正在恢复 npm 官方源...' + colors.nc);
      try {
        execSync('npm config set registry https://registry.npmjs.org/', { stdio: 'inherit' });
        console.log('  ' + colors.green + '✓ npm 源已恢复为官方地址' + colors.nc);
      } catch (err) {
        console.log('  ' + colors.red + '✗ 恢复失败: ' + err.message + colors.nc);
      }
      console.log('');
      console.log(bar);
      console.log('');
    } else {
      console.log('');
      console.log('  ' + colors.dim + '已取消卸载' + colors.nc);
      console.log('');
      console.log(bar);
      console.log('');
    }
  });
}

// ============================================================================
// 强制更新命令
// ============================================================================

function runUpdate() {
  const bar = '----------------------------------------';
  console.log('');
  console.log('[' + colors.blue + 'MyClaw' + colors.nc + '] ' + colors.blue + '自动升级检测' + colors.nc);
  console.log(bar);
  console.log('');

  const { execSync } = require('child_process');
  const registry = 'https://registry.npmjs.org';
  const pkg = '@aiyiran/myclaw';
  const maxRetries = 3;

  // 获取当前版本
  let currentVersion = 'unknown';
  try {
    currentVersion = require(path.join(__dirname, 'package.json')).version;
  } catch { }

  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      if (attempt > 1) {
        console.log('(第 ' + attempt + ' 次尝试)');
        console.log('');
      }

      // 第一步：查询最新版的 tarball URL
      console.log('> 查询最新版本...');
      const tarball = execSync(
        'npm view ' + pkg + ' dist.tarball --registry ' + registry,
        { encoding: 'utf8', timeout: 15000 }
      ).trim();

      const latestVersion = execSync(
        'npm view ' + pkg + ' version --registry ' + registry,
        { encoding: 'utf8', timeout: 15000 }
      ).trim();

      console.log('  当前版本: v' + currentVersion);
      console.log('  最新版本: v' + latestVersion);
      console.log('  Tarball:  ' + tarball);
      console.log('');

      if (currentVersion === latestVersion) {
        console.log('[' + colors.green + '已是最新' + colors.nc + '] 无需升级');
        break;
      }

      // 第二步：直接从 tarball URL 安装（绕过 @latest 解析）
      console.log('> 从 tarball 直接安装...');
      execSync('npm install -g ' + tarball, { stdio: 'inherit', timeout: 60000 });

      console.log('');
      console.log('[' + colors.green + '成功' + colors.nc + '] MyClaw 已升级: v' + currentVersion + ' → v' + latestVersion);

      if (detectPlatform() === 'wsl' || detectPlatform() === 'linux' || detectPlatform() === 'mac') {
        console.log('');
        console.log('如果需要同时升级 OpenClaw 核心，请运行:');
        console.log('  ' + colors.yellow + 'myclaw install' + colors.nc);
      }
      break;
    } catch (err) {
      if (attempt < maxRetries) {
        console.log('');
        console.log('[' + colors.yellow + '重试' + colors.nc + '] 8 秒后重试 (' + attempt + '/' + maxRetries + ')...');
        const waitCmd = require('os').platform() === 'win32' ? 'timeout /t 8 >nul' : 'sleep 8';
        try { execSync(waitCmd, { stdio: 'ignore' }); } catch { }
      } else {
        console.log('');
        console.log('[' + colors.red + '错误' + colors.nc + '] 升级失败: ' + err.message);
        console.log('');
        console.log('手动升级: npm install -g ' + pkg + '@latest --registry ' + registry);
      }
    }
  }
  console.log('');
}

// ============================================================================
// mc main — 确保 agents.list 中存在 id=main 且排在第一位
// ============================================================================

function runMain() {
  const { readConfig, writeConfig } = require('./find-config');
  const os = require('os');
  const bar = '----------------------------------------';
  console.log('');
  console.log('[mc main] 检查 main agent 配置...');
  console.log(bar);

  let config, configPath;
  try {
    ({ config, configPath } = readConfig());
  } catch (err) {
    console.error('[错误] 无法读取 openclaw.json: ' + err.message);
    process.exit(1);
  }

  if (!config.agents) config.agents = {};
  if (!Array.isArray(config.agents.list)) config.agents.list = [];

  const defaultAgentDir = (os.platform() === 'linux' ? '/root' : os.homedir())
    + '/.openclaw/agents/main/agent';

  const idx = config.agents.list.findIndex(function (a) { return a.id === 'main'; });

  if (idx === -1) {
    // 不存在，插入到最前面
    config.agents.list.unshift({ id: 'main', agentDir: defaultAgentDir });
    console.log('  ✅ 已添加 main agent: ' + defaultAgentDir);
  } else if (idx !== 0) {
    // 存在但不在第一位，移到最前面
    const entry = config.agents.list.splice(idx, 1)[0];
    config.agents.list.unshift(entry);
    console.log('  ✅ main agent 已移至第一位');
  } else {
    console.log('  ✅ main agent 已在第一位，无需修改');
  }

  writeConfig(config, configPath);
  console.log('');

  // 发送初始化消息激活 main agent
  const { execSync } = require('child_process');
  console.log('[mc main] 发送激活消息...');
  try {
    execSync('openclaw agent --agent main --message "你好!"', { stdio: 'inherit' });
  } catch (err) {
    console.error('[警告] 发送消息失败: ' + err.message);
  }

  console.log('');
  console.log('[mc main] 完成');
  console.log('');
}

// ============================================================================
// 资源管理列表
// ============================================================================

function runList() {
  const { loadManifest } = require('./patches/patch-agent');
  const bar = '----------------------------------------';

  console.log('');
  console.log('[' + colors.blue + 'MyClaw' + colors.nc + '] ' + colors.blue + '注入资源列表 (Patch Manifest)' + colors.nc);
  console.log(bar);

  const manifest = loadManifest();
  if (!manifest) {
    console.log('');
    console.log('[' + colors.yellow + '提示' + colors.nc + '] patch-manifest.json 不存在');
    console.log('  运行发布脚本或 ' + colors.yellow + 'myclaw patch' + colors.nc + ' 将自动生成');
    console.log('');
    return;
  }

  function formatStrategy(strat) {
    if (strat === 'auto') return colors.green + '[auto]' + colors.nc;
    if (strat === 'on') return colors.yellow + '[on]' + colors.nc;
    if (strat === 'off') return colors.yellow + '[off]' + colors.nc;
    if (strat === 'delete') return colors.red + '[delete]' + colors.nc;
    return '[' + strat + ']';
  }

  // === Agents ===
  console.log('');
  console.log('  ' + colors.blue + '🤖 智能体 (Agents)' + colors.nc);
  if (manifest.agents && manifest.agents.length > 0) {
    for (const a of manifest.agents) {
      console.log('  ' + padRight(a.id, 25) + formatStrategy(a.strategy));
    }
  } else {
    console.log('  (无)');
  }

  // === Skills ===
  console.log('');
  console.log('  ' + colors.blue + '🧩 技能 (Skills)' + colors.nc);
  if (manifest.skills && manifest.skills.length > 0) {
    for (const s of manifest.skills) {
      console.log('  ' + padRight(s.name, 25) + formatStrategy(s.strategy));
    }
  } else {
    console.log('  (无)');
  }

  // === Config ===
  console.log('');
  console.log('  ' + colors.blue + '⚙️  配置 (Config Patches)' + colors.nc);
  if (manifest.config && manifest.config.patches) {
    const strat = manifest.config.strategy || 'on';
    console.log('  策略: ' + formatStrategy(strat));
    for (const [key, value] of Object.entries(manifest.config.patches)) {
      console.log('  ' + padRight(key, 25) + '→ ' + JSON.stringify(value));
    }
  } else {
    console.log('  (无)');
  }

  console.log('');
  console.log(bar);
  console.log('策略说明: ' + colors.green + 'auto' + colors.nc + '=不存在才注入  '
    + colors.yellow + 'on' + colors.nc + '=始终覆盖更新  '
    + colors.yellow + 'off' + colors.nc + '=关闭不注入  '
    + colors.red + 'delete' + colors.nc + '=删除目标');
  console.log('');
}

function padRight(str, len) {
  if (str.length >= len) return str + ' ';
  return str + ' '.repeat(len - str.length);
}

// ============================================================================
// 交互式菜单（上下键选择）
// ============================================================================

// ============================================================================
// 机器选择配置
// ============================================================================
const MACHINE_ZERO = { name: '依然', claw: 'claw', desc: '依然 的机器' };

const MACHINE_CONFIG = [
  { name: 'kendy', claw: 'claw1', desc: 'kendy 的机器' },
  { name: 'Ethan', claw: 'claw2', desc: 'Ethan 的机器' },
  { name: 'HENRY', claw: 'claw3', desc: 'HENRY 的机器' },
  { name: 'Mo靖宇', claw: 'claw4', desc: 'Mo靖宇 的机器' },
  { name: '高兴', claw: 'claw5', desc: '高兴 的机器' },
  { name: '伊伊', claw: 'claw6', desc: '伊伊 的机器' },
  { name: '梓浩', claw: 'claw7', desc: '梓浩 的机器' },
  { name: '刘泓毅', claw: 'claw8', desc: '刘泓毅 的机器' },
  { name: 'Mia', claw: 'claw9', desc: 'Mia 的机器' },
  { name: '雨熙', claw: 'claw10', desc: '雨熙 的机器' },
  { name: '绍博', claw: 'claw11', desc: '绍博 的机器' },
  { name: '黄玺澄', claw: 'claw12', desc: '玺澄 的机器' },
];

function openMachine(selected) {
  console.log('');
  console.log(colors.green + '  → 已选择: ' + selected.name + ' (' + selected.claw + ')' + colors.nc);
  console.log('  ' + colors.dim + '→ 正在打开浏览器...' + colors.nc);
  const { exec } = require('child_process');
  const fs = require('fs');
  const machineUrl = 'https://' + selected.claw + '.kekouen.cn?token=aiyiran';
  const platform = os.platform();
  if (platform === 'darwin') {
    exec('open -a "Google Chrome" "' + machineUrl + '"');
  } else if (platform === 'win32') {
    const chromeCandidates = [
      path.join(process.env.PROGRAMFILES || 'C:\\Program Files', 'Google', 'Chrome', 'Application', 'chrome.exe'),
      path.join(process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)', 'Google', 'Chrome', 'Application', 'chrome.exe'),
      path.join(process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'), 'Google', 'Chrome', 'Application', 'chrome.exe'),
    ];
    const chromePath = chromeCandidates.find(function (p) { try { return fs.existsSync(p); } catch { return false; } });
    if (chromePath) {
      exec('"' + chromePath + '" "' + machineUrl + '"');
    } else {
      exec('start "" "' + machineUrl + '"', { shell: true });
    }
  } else {
    exec('xdg-open "' + machineUrl + '"');
  }
  createDesktopShortcut(machineUrl);
}

function showMachineMenu() {
  console.log('');
  console.log('  ' + colors.blue + '机器选择' + colors.nc);
  console.log('----------------------------------------');
  console.log('');
  console.log('  ' + colors.cyan + '0.' + colors.nc + ' ' + MACHINE_ZERO.name + ' (' + colors.dim + MACHINE_ZERO.claw + colors.nc + ')');
  console.log('     ' + colors.dim + MACHINE_ZERO.desc + colors.nc);
  console.log('');
  MACHINE_CONFIG.forEach((m, i) => {
    console.log('  ' + colors.cyan + (i + 1) + '.' + colors.nc + ' ' + m.name + ' (' + colors.dim + m.claw + colors.nc + ')');
    console.log('     ' + colors.dim + m.desc + colors.nc);
    console.log('');
  });
  console.log('  ' + colors.cyan + 'q.' + colors.nc + ' 返回');
  console.log('');

  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
  rl.question('请选择机器 [0-' + MACHINE_CONFIG.length + ', q 返回]: ', function (answer) {
    rl.close();
    const trimmed = answer.trim();
    if (trimmed === 'q' || trimmed === '') return;
    const choice = parseInt(trimmed);
    if (choice === 0) {
      openMachine(MACHINE_ZERO);
      return;
    }
    if (choice >= 1 && choice <= MACHINE_CONFIG.length) {
      openMachine(MACHINE_CONFIG[choice - 1]);
    } else {
      console.log('[' + colors.red + '错误' + colors.nc + '] 无效选择');
    }
  });
}

const MENU_ITEMS = [
  { key: 'machine', label: '💻机器', cmd: 'mc machine', desc: '选择不同的机器', action: showMachineMenu },
  { key: 'start', label: '🦞启动🦞', cmd: 'mc start', desc: '把你的 AI 助手叫醒，让它开始工作', action: () => { const start = require('./start'); start.run(); } },
  { key: 'restart', label: '重启', cmd: 'mc restart', desc: 'AI 助手卡住了？让它重新启动一下', action: runRestart },
  { key: 'new', label: '😊新伙伴', cmd: 'mc new', desc: '创建一个新的 AI 助手，给它取个名字', action: runNew },
  { key: 'tui', label: '新对话', cmd: 'mc tui', desc: '唤起新对话上下文', action: () => runTui(true) },
  { key: 'server', label: '🚀服务端', cmd: 'mc server', desc: '启动后端文件同步服务（守护模式，自动重启）', action: () => runServer() },
  { key: 'status', label: '网址', cmd: 'mc status', desc: '获取控制台链接，复制到浏览器打开', action: runStatus },
  {
    key: 'update', label: '升级', cmd: 'mc up', desc: '让 MyClaw 工具升级到最新版本', action: () => {
      runUpdate();
      console.log('');
      console.log('🔧 自动执行 patch 以恢复自定义配置...');
      runPatch();
      if (detectPlatform() === 'windows') runBat();
    }
  },
  { key: 'patch', label: '修复', cmd: 'mc patch', desc: '给 AI 助手装上新技能和好看的外衣', action: runPatch },
  { key: 'reinstall', label: '重装', cmd: 'mc longxia', desc: '出了大问题？把 AI 助手删了重新安装', action: runReinstall },
  { key: 'uninstall', label: '卸载', cmd: 'mc uninstall', desc: '卸载 MyClaw，恢复 npm 源地址', action: runUninstall },
  {
    key: 'wsl2reinstall', label: 'WSL2虚拟机重装', cmd: 'mc wsl2 --remote --force-phase1', desc: '强制从网络重新下载并重装 WSL2 虚拟机（仅限 Windows）', action: () => {
      process.argv.push('--remote', '--force-phase1');
      const wsl2 = require('./wsl2');
      wsl2.run();
    }
  },
  {
    key: 'safe', label: '虚拟机屏蔽', cmd: 'mc safe', desc: '禁止 WSL2 访问 Windows 盘符（仅限 Windows）', action: () => {
      const restrict = require('./restrict');
      restrict.run();
    }
  },
  { key: 'bat', label: '桌面快捷方式', cmd: 'mc bat', desc: '在桌面生成一键启动脚本（仅限 Windows）', action: runBat },
  { key: 'quit', label: '退出', cmd: 'Ctrl+C', desc: '不玩了，下次见', action: () => { console.log('  👋 再见！\n'); process.exit(0); } },
];

function runInteractiveMenu() {
  let cursor = 0;
  let handler = null;
  let resizeHandler = null;

  function render() {
    // 清屏
    process.stdout.write('\x1b[2J\x1b[H');

    const bar = '────────────────────────────────────────';
    console.log('');
    console.log(bar);
    console.log('  ' + colors.blue + 'MyClaw v' + version + colors.nc + ' - 快捷菜单');
    console.log(bar);
    console.log('  ' + colors.dim + '(↑↓ 移动  Enter 选择  Ctrl+C 退出)' + colors.nc);
    console.log('');

    MENU_ITEMS.forEach((item, i) => {
      const sel = i === cursor;
      const arrow = sel ? colors.cyan + '▶ ' + colors.nc : '  ';
      const num = colors.dim + String(i + 1) + '.' + colors.nc;
      const label = sel ? colors.bold + item.label + colors.nc : item.label;
      const cmdHint = colors.dim + item.cmd + colors.nc;
      console.log('  ' + arrow + num + ' ' + label + '  ' + cmdHint);
    });

    console.log('');
    console.log(bar);
    console.log('');

    // 说明区：显示选中项的描述
    const cur = MENU_ITEMS[cursor];
    console.log('  ' + colors.yellow + '📖 ' + cur.label + colors.nc);
    console.log('');
    console.log('  ' + colors.dim + cur.desc + colors.nc);
    console.log('');
    console.log(bar);
  }

  function onData(buf) {
    const k = buf.toString();

    if (k === '\x1b[A') { // 上
      cursor = Math.max(0, cursor - 1);
      render();
    } else if (k === '\x1b[B') { // 下
      cursor = Math.min(MENU_ITEMS.length - 1, cursor + 1);
      render();
    } else if (k === '\r' || k === '\n') { // Enter
      // 暂停菜单键盘监听，执行完后恢复
      process.stdin.removeListener('data', handler);
      process.stdin.setRawMode(false);
      process.stdin.pause();

      const item = MENU_ITEMS[cursor];
      if (item.key === 'quit') {
        item.action();
        return;
      }

      // tui 命令需要交互式输入，不走菜单的"按回车返回"逻辑
      if (item.key === 'tui') {
        console.log('');
        item.action();
        return;
      }

      console.log('');
      item.action();

      // 等待用户按回车再返回菜单
      console.log('');
      console.log(colors.dim + '  → 按回车键返回菜单...' + colors.nc);

      process.stdin.setRawMode(true);
      process.stdin.resume();
      const waitHandler = (buf) => {
        const wk = buf.toString();
        if (wk === '\r' || wk === '\n') {
          process.stdin.removeListener('data', waitHandler);
          process.stdin.on('data', handler);
          render();
        }
      };
      process.stdin.on('data', waitHandler);
    } else if (k === '\x03') {
      process.stdin.setRawMode(false);
      process.stdin.removeListener('data', handler);
      if (resizeHandler) process.stdout.removeListener('resize', resizeHandler);
      console.log('\n  👋 再见！\n');
      process.exit(0);
    }
  }

  process.stdin.setRawMode(true);
  process.stdin.resume();
  handler = onData;
  process.stdin.on('data', handler);

  resizeHandler = () => render();
  process.stdout.on('resize', resizeHandler);

  render();
}

// ============================================================================
// 帮助信息
// ============================================================================

// ============================================================================
// inject 交互菜单
// ============================================================================

const INJECT_MENU = [
  { key: '1', cmd: 'inject-minimax', desc: '注入 MiniMax 模型配置' },
  { key: '2', cmd: 'inject-zai', desc: '注入智谱 GLM 模型配置' },
  { key: '3', cmd: 'inject-image', desc: '注入图像生成模型配置 (vveai)' },
  { key: '4', cmd: 'inject-search', desc: '注入 Tavily 搜索插件配置' },
  { key: '5', cmd: 'inject-token', desc: '设置 Gateway Token 为 aiyiran' },
  { key: '6', cmd: 'inject-workspaceAndSoul', desc: '替换默认 workspace 的 SOUL.md' },
  { key: '7', cmd: 'inject-tooldeny', desc: 'deny image_generate + music_generate 内置工具' },
  { key: '8', cmd: 'inject-controlui', desc: '注入 controlUi 配置 (allowInsecureAuth + allowedOrigins + dangerouslyDisableDeviceAuth)' },
  { key: 'a', cmd: 'all', desc: '执行以上全部注入' },
];

function runInjectCommand(cmd, extraArgs) {
  const modules = {
    'inject-minimax': './inject-minimax',
    'inject-zai': './inject-zai',
    'inject-image': './inject-image',
    'inject-search': './inject-search',
    'inject-token': './inject-token',
    'inject-workspaceAndSoul': './inject-workspaceAndSoul',
    'inject-tooldeny': './inject-tooldeny',
    'inject-controlui': './inject-controlui',
  };
  const mod = require(modules[cmd]);
  mod.run(extraArgs || []);
  console.log('🔄 正在重启 Gateway 使配置生效...');
  console.log('');
}

function showInjectMenu() {
  console.log('');
  console.log(colors.blue + '📦 注入脚本选择' + colors.nc);
  console.log('----------------------------------------');
  console.log('');
  for (const item of INJECT_MENU) {
    console.log('  ' + colors.cyan + item.key + colors.nc + ') ' + item.desc);
  }
  console.log('  ' + colors.cyan + '0' + colors.nc + ') 返回');
  console.log('');

  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
  rl.question('请选择 [0-' + INJECT_MENU.length + '/a]: ', function (answer) {
    rl.close();
    const choice = answer.trim().toLowerCase();

    if (choice === '0' || choice === '') {
      return;
    }

    if (choice === 'a') {
      for (const item of INJECT_MENU) {
        if (item.cmd !== 'all') {
          try {
            console.log('');
            console.log('→ ' + item.desc + '...');
            runInjectCommand(item.cmd);
          } catch (err) {
            console.log('  ❌ 失败: ' + err.message);
          }
        }
      }
      runRestart();
      return;
    }

    const found = INJECT_MENU.find(m => m.key === choice);
    if (!found) {
      console.log('[' + colors.red + '错误' + colors.nc + '] 无效选择: ' + choice);
      return;
    }

    runInjectCommand(found.cmd, args.slice(1));
    runRestart();
  });
}

// ============================================================================
// Server 后端服务
// ============================================================================

const rl = require('readline');

function generateRandomName() {
  const letters = 'abcdefghijklmnopqrstuvwxyz';
  let name = '';
  for (let i = 0; i < 3; i++) {
    name += letters[Math.floor(Math.random() * letters.length)];
  }
  for (let i = 0; i < 3; i++) {
    name += String(Math.floor(Math.random() * 5) + 5);
  }
  return name;
}

async function ensureConfig(customName) {
  const fs = require('fs');
  const configPath = path.join(__dirname, 'server', 'config.json');

  if (fs.existsSync(configPath)) {
    return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
  }

  const clawName = customName || generateRandomName();
  const config = {
    claw: clawName
  };

  fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
  console.log('[Server] 配置文件已创建: ' + configPath);
  console.log('');
  return config;
}

function runSyncTemplates() {
  const http = require('http');
  const fs = require('fs');
  const apiPort = process.platform === 'linux' ? '8080' : '18800';

  // --force：删除本地 index，强制重新比对
  if (args.includes('--force')) {
    const localIndex = path.join(os.homedir(), '.openclaw', 'skills', 'yiran-playground-template-use', 'template-index.json');
    if (fs.existsSync(localIndex)) {
      fs.unlinkSync(localIndex);
      console.log(colors.yellow + '[muban] 已清除本地 index，强制重新检查' + colors.nc);
    }
  }

  function apiGet(path, cb) {
    http.get('http://127.0.0.1:' + apiPort + path, (res) => {
      let data = '';
      res.on('data', chunk => data += chunk);
      res.on('end', () => {
        try { cb(null, JSON.parse(data)); } catch (e) { cb(e); }
      });
    }).on('error', cb);
  }

  console.log(colors.blue + '[muban]' + colors.nc + ' 正在检查模板更新...');

  apiGet('/api/sync-templates', (err, res) => {
    if (err) {
      console.error(colors.red + '✗ 无法连接到本地服务（是否已运行 mc sync？）: ' + err.message + colors.nc);
      process.exit(1);
    }
    if (res.error) {
      console.error(colors.red + '✗ ' + res.error + colors.nc);
      process.exit(1);
    }
    if (!res.changed) {
      console.log(colors.green + '✓ 模板已是最新，无需更新' + colors.nc);
      return;
    }
    if (!res.syncing) {
      console.log(colors.green + '✓ 更新完成' + colors.nc);
      return;
    }

    // 后台正在同步，轮询进度
    console.log(colors.yellow + '⟳ 开始下载模板文件...' + colors.nc);
    const deadline = Date.now() + 10 * 60 * 1000;

    function poll() {
      if (Date.now() > deadline) {
        console.error(colors.red + '✗ 同步超时（10分钟）' + colors.nc);
        process.exit(1);
      }
      setTimeout(() => {
        apiGet('/api/sync-status', (err, s) => {
          if (err) { poll(); return; }
          if (s.running) {
            process.stdout.write('\r  ' + colors.yellow + '⟳ 进度: ' + s.completed.length + '/' + s.total + colors.nc + '          ');
            poll();
          } else {
            process.stdout.write('\n');
            const r = s.result || {};
            const parts = [];
            if (r.added && r.added.length) parts.push('新增 ' + r.added.length + ' 个');
            if (r.updated && r.updated.length) parts.push('更新 ' + r.updated.length + ' 个');
            console.log(colors.green + '✓ 同步完成' + (parts.length ? '：' + parts.join('，') : '') + colors.nc);
          }
        });
      }, 2000);
    }
    poll();
  });
}

function runSync(workspaceName) {
  const { spawn } = require('child_process');
  const fs = require('fs');

  // 用户目录下的服务目录
  const targetDir = path.join(os.homedir(), '.openclaw', 'myclaw', 'server');
  const targetPyPath = path.join(targetDir, 'sync_workspace.py');
  const targetConfigPath = path.join(targetDir, 'config.json');
  const sourcePyPath = path.join(__dirname, 'server', 'sync_workspace.py');
  const sourceSchemaPath = path.join(__dirname, 'server', 'artifacts_schema.py');

  // 确保目标目录存在
  if (!fs.existsSync(targetDir)) {
    fs.mkdirSync(targetDir, { recursive: true });
  }

  // 同步 py 文件
  fs.copyFileSync(sourcePyPath, targetPyPath);
  fs.copyFileSync(sourceSchemaPath, path.join(targetDir, 'artifacts_schema.py'));

  // 读取配置（如果不存在则创建）
  let config;
  if (fs.existsSync(targetConfigPath)) {
    config = JSON.parse(fs.readFileSync(targetConfigPath, 'utf-8'));
  } else {
    config = { claw: generateRandomName() };
    fs.writeFileSync(targetConfigPath, JSON.stringify(config, null, 2), 'utf-8');
  }

  const agent = workspaceName ? `workspace-${workspaceName}` : 'workspace';

  console.log('[Sync] 单次同步: ' + agent);
  console.log('[Sync] CLAW_NAME: ' + config.claw);

  const child = spawn('python3', [targetPyPath, '--agent', agent], {
    stdio: 'inherit',
    shell: false,
    env: { ...process.env, CLAW_NAME: config.claw }
  });

  child.on('error', (err) => {
    console.error('[' + colors.red + '错误' + colors.nc + '] 同步失败: ' + err.message);
    process.exit(1);
  });

  child.on('exit', (code) => {
    if (code !== 0) {
      console.error('[' + colors.red + '错误' + colors.nc + '] 同步异常退出，代码: ' + code);
      process.exit(code);
    }
  });
}

async function runServer(name) {
  const { spawn, execSync: execSyncLocal } = require('child_process');
  const fs = require('fs');

  // 用户目录下的服务目录
  const targetDir = path.join(os.homedir(), '.openclaw', 'myclaw', 'server');
  const targetPyPath = path.join(targetDir, 'sync_workspace.py');
  const targetGuardPath = path.join(targetDir, 'myclaw-guard.sh');
  const targetConfigPath = path.join(targetDir, 'config.json');
  const sourcePyPath = path.join(__dirname, 'server', 'sync_workspace.py');
  const sourceSchemaPath = path.join(__dirname, 'server', 'artifacts_schema.py');
  const sourceGuardPath = path.join(__dirname, 'server', 'myclaw-guard.sh');
  const pidFile = path.join(os.homedir(), '.openclaw', '.myclaw-sync.pid');

  // 0. 杀死旧的 sync_workspace 进程（通过 PID 文件）
  if (fs.existsSync(pidFile)) {
    try {
      const oldPid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
      if (oldPid && !isNaN(oldPid)) {
        try { process.kill(oldPid, 'SIGTERM'); } catch (e) { /* 不存在则忽略 */ }
        console.log('[Server] 已终止旧进程 PID=' + oldPid);
        // 等待旧进程退出并释放端口（最多 5 秒）
        await new Promise(resolve => {
          let waited = 0;
          const interval = setInterval(() => {
            try {
              process.kill(oldPid, 0); // 检查进程是否还存在
              waited += 200;
              if (waited >= 5000) { clearInterval(interval); resolve(); }
            } catch {
              clearInterval(interval); resolve(); // 进程已退出
            }
          }, 200);
        });
      }
    } catch (e) { /* 读取失败忽略 */ }
    try { fs.unlinkSync(pidFile); } catch (e) { }
  }

  // 1. 创建目标目录
  if (!fs.existsSync(targetDir)) {
    fs.mkdirSync(targetDir, { recursive: true });
    console.log('[Server] 创建目录: ' + targetDir);
  }

  // 1b. 创建日志目录（与 server 同级）
  const logDir = path.join(os.homedir(), '.openclaw', 'myclaw', 'log');
  if (!fs.existsSync(logDir)) {
    fs.mkdirSync(logDir, { recursive: true });
    console.log('[Server] 创建日志目录: ' + logDir);
  }

  // 2. 覆盖 py 文件 & guard 脚本（确保最新）
  fs.copyFileSync(sourcePyPath, targetPyPath);
  fs.copyFileSync(sourceSchemaPath, path.join(targetDir, 'artifacts_schema.py'));
  console.log('[Server] 同步脚本: ' + targetPyPath);
  fs.copyFileSync(sourceGuardPath, targetGuardPath);
  fs.chmodSync(targetGuardPath, 0o755);
  console.log('[Server] 守卫脚本: ' + targetGuardPath);

  // 3. 确保配置文件存在（如果已有就不动）
  let config;
  if (fs.existsSync(targetConfigPath)) {
    config = JSON.parse(fs.readFileSync(targetConfigPath, 'utf-8'));
    console.log('[Server] 已有配置: ' + targetConfigPath);
  } else if (name) {
    config = { claw: name };
    fs.writeFileSync(targetConfigPath, JSON.stringify(config, null, 2), 'utf-8');
    console.log('[Server] 创建配置: ' + targetConfigPath);
  } else {
    console.error('[' + colors.red + '错误' + colors.nc + '] 未找到配置文件，请先指定名称: mc server <name>');
    process.exit(1);
  }

  console.log('[Server] CLAW_NAME: ' + config.claw);
  console.log('[Server] 守护模式: 已启用');
  console.log('');

  // 4. 检查并安装 Python 依赖
  console.log('[Server] 检查依赖...');
  try {
    execSync('python3 -c "from watchdog.observers import Observer; from qiniu import Auth"', { stdio: 'pipe' });
    console.log('[Server] 依赖已满足');
  } catch (e) {
    console.log('[Server] 正在安装依赖 watchdog qiniu...');
    try {
      execSync('pip3 install watchdog qiniu --trusted-host mirrors.aliyun.com -i https://mirrors.aliyun.com/pypi/simple/', { stdio: 'inherit' });
      console.log('[Server] 依赖安装完成');
    } catch (err) {
      console.error('[' + colors.red + '错误' + colors.nc + '] 依赖安装失败，请检查网络');
      process.exit(1);
    }
  }
  console.log('');

  let child;

  function startProcess() {
    console.log('[Server] 启动服务...');
    // Linux 服务器用 8080（对接 nginx /sync 规则），Mac 本地用 18800
    const apiPort = process.platform === 'linux' ? '8080' : '18800';
    child = spawn('python3', [targetPyPath], {
      stdio: 'inherit',
      shell: false,
      env: { ...process.env, CLAW_NAME: config.claw, MYCLAW_API_PORT: apiPort }
    });

    child.on('error', (err) => {
      console.error('[' + colors.red + '错误' + colors.nc + '] 启动失败: ' + err.message);
    });

    child.on('exit', (code, signal) => {
      // 被 SIGTERM 杀死（下一次 mc server 启动时）→ 不重启
      if (signal === 'SIGTERM') {
        console.log('[Server] 服务已被外部终止，不再重启');
        process.exit(0);
        return;
      }
      if (code !== null && code !== 0) {
        console.log('[' + colors.yellow + '警告' + colors.nc + '] 服务异常退出，代码: ' + code + '，3秒后重启...');
        setTimeout(startProcess, 3000);
      } else {
        console.log('[Server] 服务已正常停止');
      }
    });

    // 记录子进程 PID 到文件
    if (child.pid) {
      fs.writeFileSync(pidFile, String(child.pid), 'utf-8');
    }
  }

  startProcess();
}

// ============================================================================
// Agent 批量删除（CLI 交互式）
// ============================================================================

function runDelete() {
  const {
    listAllAgents, deleteRegisteredAgent, deleteOrphanAgent, cleanAgentDirs,
  } = require('./delete_agents');

  const isDeep = args.includes('--deep'); // 加 --deep 则自动执行阶段2，不询问
  const directId = (args[1] && !args[1].startsWith('--')) ? args[1] : null; // 直接指定 agent 名
  const prefixIdx = args.indexOf('--prefix');
  const prefix = prefixIdx !== -1 ? (args[prefixIdx + 1] || null) : null; // 前缀批量删除

  const G = colors.green;
  const Y = colors.yellow;
  const R = colors.red;
  const B = colors.blue;
  const D = colors.dim;
  const NC = colors.nc;

  const BAR = '─'.repeat(52);

  function fmtDate(ms) {
    if (!ms) return '未知';
    const d = new Date(ms);
    const pad = n => String(n).padStart(2, '0');
    return `${d.getFullYear()}/${pad(d.getMonth() + 1)}/${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
  }

  function fmtStatus(ag) {
    return ag.inJson ? G + '● 已注册' + NC : Y + '○ 孤立  ' + NC;
  }

  function printTable(agents) {
    console.log('');
    console.log(BAR);
    console.log(B + '  🗑  批量删除伙伴' + NC);
    console.log(BAR);
    console.log(D + '  序号  状态       ID                         会话  最后更新' + NC);
    console.log(D + '  ─────────────────────────────────────────────────────' + NC);
    agents.forEach((ag, i) => {
      const num = String(i + 1).padStart(3, ' ');
      const idPad = ag.id.padEnd(28, ' ').slice(0, 28);
      const cnt = String(ag.sessionCount).padStart(4, ' ');
      const updated = fmtDate(ag.lastUpdated);
      console.log(`  ${num}.  ${fmtStatus(ag)} ${idPad} ${cnt}  ${D}${updated}${NC}`);
    });
    console.log(BAR);
    console.log(D + '  ' + G + '●' + NC + D + ' 已注册（JSON）   ' + Y + '○' + NC + D + ' 孤立（仅有文件夹）' + NC);
    console.log('');
  }

  // ── 解析序号字符串 ──
  function parseSelection(input, max) {
    if (input.trim().toLowerCase() === 'all') {
      return Array.from({ length: max }, (_, i) => i);
    }
    const indices = new Set();
    for (const part of input.split(',')) {
      const t = part.trim();
      const range = t.match(/^(\d+)-(\d+)$/);
      if (range) {
        const a = parseInt(range[1], 10) - 1;
        const b = parseInt(range[2], 10) - 1;
        for (let i = a; i <= b; i++) {
          if (i >= 0 && i < max) indices.add(i);
        }
      } else if (/^\d+$/.test(t)) {
        const n = parseInt(t, 10) - 1;
        if (n >= 0 && n < max) indices.add(n);
      }
    }
    return [...indices].sort((a, b) => a - b);
  }

  // ── 主流程 ──
  const agents = listAllAgents();

  if (agents.length === 0) {
    console.log(Y + '没有找到任何 Agent。' + NC);
    return;
  }

  const rl = directId
    ? { close: () => {}, question: (_, cb) => cb('') }
    : readline.createInterface({ input: process.stdin, output: process.stdout });

  function step1() {
    rl.question(
      '  输入序号选择要删除的伙伴\n'
      + '  ' + D + '（如: 1,3  或  1-3  或  all，直接回车取消）' + NC + '\n'
      + '  > ',
      function (answer) {
        const raw = (answer || '').trim();
        if (!raw) {
          console.log(D + '\n  已取消。' + NC + '\n');
          rl.close();
          return;
        }

        const indices = parseSelection(raw, agents.length);
        if (indices.length === 0) {
          console.log(R + '  无效输入，请重试。' + NC);
          step1();
          return;
        }

        const selected = indices.map(i => agents[i]);
        step2(selected);
      }
    );
  }

  function step2(selected) {
    console.log('');
    console.log(BAR);
    console.log(R + '  ⚠  确认删除以下 ' + selected.length + ' 个伙伴（移入回收站）' + NC);
    console.log(BAR);
    selected.forEach(ag => {
      const tag = ag.inJson ? '' : ' ' + Y + '[孤立]' + NC;
      console.log(`  • ${R}${ag.id}${NC}${tag}  ${D}${ag.sessionCount} 个会话  最后更新: ${fmtDate(ag.lastUpdated)}${NC}`);
    });
    console.log('');

    rl.question('  输入 ' + R + 'YES' + NC + ' 确认，其他任意键取消: ', function (ans) {
      if ((ans || '').trim().toUpperCase() !== 'YES') {
        console.log(D + '\n  已取消。' + NC + '\n');
        rl.close();
        return;
      }
      step3(selected); // rl 留到阶段2询问后再关
    });
  }

  // ── 阶段1：注销 + 清理主数据（对齐官方最小删除标准） ──
  function step3(selected) {
    console.log('');
    console.log(BAR);
    console.log(B + '  阶段1：注销 + 删除主数据...' + NC);
    console.log(BAR);

    const stage1Ok = [];
    let done = 0;
    for (const ag of selected) {
      done++;
      process.stdout.write(`  [${done}/${selected.length}] ${ag.id}... `);

      if (ag.inJson) {
        const res = deleteRegisteredAgent(ag.id);
        if (res.ok) {
          stage1Ok.push(ag);
          console.log(G + '✅ 已注销' + NC);
        } else {
          console.log(R + '❌ 失败: ' + res.error + NC);
        }
      } else {
        const res = deleteOrphanAgent(ag);
        if (res.ok) {
          stage1Ok.push(ag);
          const method = res.results[0] && res.results[0].method === 'rm'
            ? Y + '⚠ 已直接删除（未找到回收站）' + NC
            : G + '✅ 已移至回收站' + NC;
          console.log(method);
        } else {
          const err = (res.results || []).map(r => r.error).filter(Boolean).join('; ');
          console.log(R + '❌ 失败: ' + err + NC);
        }
      }
    }

    if (stage1Ok.length === 0) {
      rl.close();
      showDone(0, 0);
      return;
    }

    if (isDeep) {
      // --deep 参数：自动执行阶段2
      rl.close();
      runStage2(stage1Ok, true);
    } else {
      // 交互式询问
      console.log('');
      rl.question(
        '  ' + B + '阶段2' + NC + '：是否同时清理会话历史？'
        + D + '（会话记录将移入回收站）' + NC + '\n'
        + '  ' + D + '（y 清理 / 直接回车跳过）' + NC + ' > ',
        function (ans) {
          rl.close();
          if ((ans || '').trim().toLowerCase() === 'y') {
            runStage2(stage1Ok, false);
          } else {
            showDone(stage1Ok.length, 0);
          }
        }
      );
    }
  }

  // ── 阶段2：清理 sessions/ 残余目录 ──
  function runStage2(agents, silent) {
    if (!silent) {
      console.log('');
      console.log(BAR);
      console.log(B + '  阶段2：清理会话历史...' + NC);
      console.log(BAR);
    }
    let cleaned = 0;
    for (const ag of agents) {
      const res = cleanAgentDirs(ag.id);
      if (res.ok) cleaned++;
      if (!silent) {
        process.stdout.write(`  ${ag.id}... `);
        console.log(res.ok ? G + '✅' + NC : R + '❌' + NC);
      }
    }
    showDone(agents.length, cleaned);
  }

  function showDone(stage1Count, stage2Count) {
    console.log('');
    console.log(BAR);
    if (stage2Count > 0) {
      console.log(G + '  完成！阶段1: ' + stage1Count + ' 个  阶段2: ' + stage2Count + ' 个（会话已清理）' + NC);
    } else {
      console.log(G + '  完成！已处理 ' + stage1Count + ' 个伙伴。' + NC);
    }
    console.log(BAR);
    console.log('');
  }

  if (directId) {
    const target = agents.find(a => a.id === directId);
    if (!target) {
      console.log('');
      console.log(R + '  找不到 Agent: "' + directId + '"' + NC);
      console.log(D + '  可用 mc delete 查看完整列表' + NC);
      console.log('');
      process.exit(1);
      return;
    }
    console.log('');
    console.log(BAR);
    console.log(R + '  ⚠  直接删除' + (isDeep ? '（含会话历史）' : '') + NC);
    console.log(BAR);
    const tag = target.inJson ? '' : ' ' + Y + '[孤立]' + NC;
    console.log(`  • ${R}${target.id}${NC}${tag}  ${D}${target.sessionCount} 个会话  最后更新: ${fmtDate(target.lastUpdated)}${NC}`);
    console.log('');
    step3([target]);
  } else if (prefix) {
    const matched = agents.filter(a => a.id.startsWith(prefix));
    if (matched.length === 0) {
      console.log('');
      console.log(R + '  没有找到前缀为 "' + prefix + '" 的 Agent' + NC);
      console.log(D + '  可用 mc delete 查看完整列表' + NC);
      console.log('');
      process.exit(1);
      return;
    }
    console.log('');
    console.log(BAR);
    console.log(B + '  🔍  前缀匹配: "' + prefix + '"' + (isDeep ? '  ' + R + '（含会话历史）' + NC : '') + NC);
    console.log(BAR);
    matched.forEach(ag => {
      const tag = ag.inJson ? '' : ' ' + Y + '[孤立]' + NC;
      console.log(`  • ${R}${ag.id}${NC}${tag}  ${D}${ag.sessionCount} 个会话  最后更新: ${fmtDate(ag.lastUpdated)}${NC}`);
    });
    console.log('');
    console.log('  共 ' + R + matched.length + NC + ' 个伙伴');
    console.log('');
    rl.question('  输入 ' + R + '1' + NC + ' 确认全部删除，其他任意键取消: ', function (ans) {
      if ((ans || '').trim() !== '1') {
        console.log(D + '\n  已取消。' + NC + '\n');
        rl.close();
        return;
      }
      step3(matched);
    });
  } else {
    printTable(agents);
    step1();
  }
}

// ── mc list-agents：输出 Agent 列表（供 API 复用） ──
function runListAgents() {
  const { listAllAgents } = require('./delete_agents');
  const isJson = args.includes('--json');
  const agents = listAllAgents();

  if (isJson) {
    console.log(JSON.stringify({ agents }, null, 2));
    return;
  }

  const G = colors.green;
  const Y = colors.yellow;
  const D = colors.dim;
  const NC = colors.nc;

  console.log('');
  console.log('序号  状态       ID                         会话  最后更新');
  console.log('─'.repeat(62));
  agents.forEach((ag, i) => {
    const num = String(i + 1).padStart(3);
    const idPad = ag.id.padEnd(28).slice(0, 28);
    const cnt = String(ag.sessionCount).padStart(4);
    const date = ag.lastUpdated ? new Date(ag.lastUpdated).toLocaleDateString('zh-CN') : '未知';
    const status = ag.inJson ? G + '● 已注册' + NC : Y + '○ 孤立  ' + NC;
    console.log(`${num}.  ${status} ${idPad} ${cnt}  ${D}${date}${NC}`);
  });
  console.log('');
  console.log(`共 ${agents.length} 个 Agent（${agents.filter(a => a.inJson).length} 已注册，${agents.filter(a => !a.inJson).length} 孤立）`);
  console.log('');
}

// ── mc trash-paths：将路径移入回收站（供 API 复用） ──
function runTrashPaths() {
  const { trashPath } = require('./delete_agents');
  // args[0] = 'trash-paths'，后续都是路径
  const paths = args.slice(1).filter(Boolean);

  if (paths.length === 0) {
    console.error('[错误] 用法: mc trash-paths <path1> [path2] ...');
    process.exit(1);
  }

  const isJson = args.includes('--json');
  const results = paths
    .filter(p => p !== '--json')
    .map(p => ({ path: p, ...trashPath(p) }));

  if (isJson) {
    const ok = results.every(r => r.ok);
    console.log(JSON.stringify({ ok, results }, null, 2));
    process.exit(ok ? 0 : 1);
    return;
  }

  for (const r of results) {
    if (r.ok) {
      const detail = r.dest ? ' → ' + r.dest : (r.note ? ' (' + r.note + ')' : '');
      console.log(colors.green + '✅' + colors.nc + ' ' + r.path + detail);
    } else {
      console.log(colors.red + '❌' + colors.nc + ' ' + r.path + '  ' + r.error);
    }
  }
}

function showHelp() {
  console.log('');
  console.log('MyClaw - 学生友好的 OpenClaw 工具');
  console.log('----------------------------------------');
  console.log('');
  console.log('用法:');
  console.log('  myclaw <command>');
  console.log('');
  console.log('向导 (交互式):');
  console.log('  prepare    智能初始化（环境检测 + 安装 + Patch）');
  console.log('  new        创建新 Agent（无参数=向导，有参数=一键）');
  console.log('  weixin     微信接入向导（步骤引导 + 教学说明）');
  console.log('  rebind     微信换绑向导（清空、扫码、换Agent）');
  console.log('');
  console.log('命令:');
  console.log('  start      智能启动（图标 & 命令行通用入口）');
  console.log('  install,i  安装 OpenClaw 服务');
  console.log('  longxia    重装 OpenClaw (清理缓存 + 锁定版本)');
  console.log('  uninstall  卸载 MyClaw (恢复 npm 官方源)');
  console.log('  status     获取控制台网址');
  console.log('  update     自动升级 MyClaw 到最新版本');
  console.log('  up         升级 + 刷新桌面快捷方式 (= update + bat)');
  console.log('  open       打开浏览器控制台（自动带 token）');
  console.log('  tui        唤起新对话上下文 (用法: mc tui <agentname>)');
  console.log('  delete     批量删除 Agent（交互式勾选 + 二次确认）');
  console.log('  muban                   更新本地课程模板（从 CDN 增量下载）');
  console.log('  list-agents             列出所有 Agent（--json 输出机器可读）');
  console.log('  trash-paths <p1> <p2>   将路径移入回收站（跨平台）');
  console.log('  fix        兜底修复（自动补装 WSL + Chrome，仅限 Windows）');
  console.log('  wsl2       WSL2 一键安装/修复 (仅限 Windows, 可选: --cli, --remote)');
  console.log('  safe       开启虚拟机屏蔽 (禁止自动挂载 Windows 盘符)');
  console.log('  bat        在桌面生成一键启动脚本 (仅限 Windows)');
  console.log('  list       查看注入资源管理列表（智能体/技能/配置）');
  console.log('  pull       从 ~/.openclaw 拉取最新资源到源目录（开发用）');
  console.log('  patch      注入 MyClaw UI + 技能 + 智能体 + 配置');
  console.log('  unpatch    回滚 UI 注入（恢复原版）');
  console.log('  inject     交互选择要执行的注入脚本');
  console.log('  inject-minimax          注入 MiniMax 模型配置 (可选: --key sk-xxx, -f 强制清理其他模型)');
  console.log('  inject-zai              注入智谱 GLM 模型配置 (可选: --key xxx, -f 强制清理其他模型)');
  console.log('  inject-image            注入图像生成模型配置 (基于 vveai)');
  console.log('  inject-token            设置 Gateway Token 为 aiyiran');
  console.log('  inject-search           注入 Tavily 搜索插件配置');
  console.log('  inject-workspaceAndSoul 替换默认 workspace 的 SOUL.md 提示词');
  console.log('  inject-tooldeny         deny image_generate + music_generate 内置工具');
  console.log('  inject-controlui        注入 controlUi 配置 (allowInsecureAuth + allowedOrigins)');
  console.log('  all        完整升级: up + patch + server + restart');
  console.log('  restart    重启 OpenClaw Gateway');
  console.log('  nuke       ⚠️  核弹重置 (需加 --dangerous): 清除数据 + onboard + up + patch + restart');
  console.log('  help       显示帮助信息');
  console.log('');
  console.log('示例:');
  console.log('  myclaw prepare                      # 智能初始化（推荐新用户首先执行）');
  console.log('  myclaw weixin                       # 打开微信接入向导');
  console.log('  myclaw install                      # 安装 OpenClaw');
  console.log('  myclaw status                       # 获取控制台网址');
  console.log('  myclaw new helper                   # 创建名为 helper 的 Agent');
  console.log('  myclaw open                         # 打开控制台（自动读取 token）');
  console.log('  myclaw open --token kakaxi-2026      # 指定 token 打开');
  console.log('  myclaw open http://x.x.x.x:18789    # 打开指定 URL');
  console.log('  myclaw patch                        # 注入 UI 扩展');
  console.log('  myclaw uninstall                    # 卸载 MyClaw（恢复 npm 源）');
  console.log('  myclaw restart                      # 重启 Gateway');
  console.log('');
  console.log('跨平台: macOS / Linux / Windows (PowerShell/CMD)');
  console.log('');
}

// ============================================================================
// 主逻辑
// ============================================================================

// --json 模式时 banner 写 stderr，保证 stdout 只有纯 JSON
if (args.includes('--json')) {
  process.stderr.write(colors.blue + 'MyClaw v' + version + colors.nc + '\n');
} else {
  console.log(colors.blue + 'MyClaw v' + version + colors.nc);
}

if (!command) {
  // 无参数 → 进入交互式菜单
  runInteractiveMenu();
} else if (command === 'help' || command === '--help' || command === '-h') {
  showHelp();
} else if (command === 'prepare') {
  runPrepare();
} else if (command === 'weixin') {
  runWeixin();
} else if (command === 'rebind') {
  runRebind();
} else if (command === 'install' || command === 'i') {
  runInstall();
} else if (command === 'longxia') {
  runReinstall();
} else if (command === 'status') {
  runStatus();
} else if (command === 'new') {
  runNew();
} else if (command === 'update') {
  runUpdate();
} else if (command === 'up') {
  runUpdate();
  // 更新后自动 patch，恢复 UI 注入和自定义配置
  console.log('');
  console.log('🔧 自动执行 patch 以恢复自定义配置...');
  runPatch();
  if (detectPlatform() === 'windows') {
    runBat();
  }
} else if (command === 'all') {
  // 完整升级：升级包 → patch → server → restart
  console.log('[mc all] 开始完整升级流程...');
  console.log('');

  // 清理竞争者
  const rivalPath = '/root/sync_workspace.py';
  try {
    require('child_process').execSync(
      'pkill -f "' + rivalPath + '" 2>/dev/null; rm -f "' + rivalPath + '"',
      { shell: true, stdio: 'pipe' }
    );
    console.log('[mc all] 已清理竞争脚本: ' + rivalPath);
  } catch (e) { /* 文件不存在或无权限，忽略 */ }
  console.log('');
  console.log('[1/4] mc up — 升级包 + patch');
  runUpdate();
  runPatch();
  if (detectPlatform() === 'windows') {
    runBat();
  }
  console.log('');
  console.log('[2/4] mc restart — 重启 Gateway');
  runRestart();
  console.log('');
  console.log('[3/4] mc server — 更新 Python 守护进程');
  runServer(args[1]);
  console.log('');
  console.log('[4/4] 完成 ✅');
} else if (command === 'start') {
  runStart();
} else if (command === 'open') {
  runOpen();
} else if (command === 'tui') {
  runTui();
} else if (command === 'delete' || command === 'del') {
  runDelete();
} else if (command === 'list-agents') {
  runListAgents();
} else if (command === 'trash-paths') {
  runTrashPaths();
} else if (command === 'fix') {
  runFix();
} else if (command === 'wsl2') {
  runWsl2();
} else if (command === 'safe') {
  runRestrict();
} else if (command === 'launch') {
  runLaunch();
} else if (command === 'bat') {
  runBat();
} else if (command === 'list') {
  runList();
} else if (command === 'pull') {
  const pull = require('./pull');
  pull.run();
} else if (command === 'patch') {
  runPatch();
} else if (command === 'unpatch') {
  runUnpatch();
} else if (command === 'uninstall') {
  runUninstall();
} else if (command === 'restart') {
  runRestart();
} else if (command === 'inject') {
  showInjectMenu();
} else if (command === 'inject-minimax') {
  const minimax = require('./injects/inject-minimax');
  minimax.run(args.slice(1));
  // 注入完成后自动重启 Gateway
  console.log('🔄 正在重启 Gateway 使配置生效...');
  console.log('');
  runRestart();
} else if (command === 'inject-zai') {
  const zai = require('./injects/inject-zai');
  zai.run(args.slice(1));
  console.log('🔄 正在重启 Gateway 使配置生效...');
  console.log('');
  runRestart();
} else if (command === 'inject-image') {
  const image = require('./injects/inject-image');
  image.run(args.slice(1));
  console.log('🔄 正在重启 Gateway 使配置生效...');
  console.log('');
  runRestart();
} else if (command === 'inject-token') {
  const token = require('./injects/inject-token');
  token.run(args.slice(1));
  console.log('🔄 正在重启 Gateway 使配置生效...');
  console.log('');
  runRestart();
} else if (command === 'inject-search') {
  const search = require('./injects/inject-search');
  search.run(args.slice(1));
  console.log('🔄 正在重启 Gateway 使配置生效...');
  console.log('');
  runRestart();
} else if (command === 'inject-workspaceAndSoul') {
  const soul = require('./injects/inject-workspaceAndSoul');
  soul.run();
  console.log('🔄 正在重启 Gateway 使配置生效...');
  console.log('');
  runRestart();
} else if (command === 'inject-tooldeny') {
  const tooldeny = require('./injects/inject-tooldeny');
  tooldeny.run(args.slice(1));
  console.log('🔄 正在重启 Gateway 使配置生效...');
  console.log('');
  runRestart();
} else if (command === 'inject-controlui') {
  const controlui = require('./injects/inject-controlui');
  controlui.run(args.slice(1));
  console.log('🔄 正在重启 Gateway 使配置生效...');
  console.log('');
  runRestart();
} else if (command === 'nuke') {
  runNuke(args.slice(1));
} else if (command === 'lock4.2') {
  runLock42();
} else if (command === 'server') {
  runServer(args[1]);  // args[1] 是可选的 name
} else if (command === 'sync') {
  runSync(args[1]);  // args[1] 是可选的 workspace 名称
} else if (command === 'muban') {
  runSyncTemplates();
} else if (command === 'init') {
  console.log('[mc init] 开始初始化流程...');
  console.log('');
  console.log('[1/4] mc up — 升级');
  runUpdate();
  console.log('');
  console.log('[2/4] mc patch — 注入 UI + 技能');
  runPatch();
  console.log('');
  console.log('[2.5/4] inject-minimax --default — 注入 MiniMax 模型并设为默认');
  try {
    const minimax = require('./injects/inject-minimax');
    minimax.run(['--default']);
  } catch (err) {
    console.log('[mc init] ⚠ inject-minimax 失败: ' + err.message);
  }
  console.log('');
  console.log('[3/4] mc server — 启动同步服务');
  runServer(args[1]);
  console.log('');
  console.log('[4/4] mc main — 启动主流程');
  runMain();
} else if (command === 'main') {
  runMain();
} else if (command === 'test') {
  runTest();
} else {
  console.error('[' + colors.red + '错误' + colors.nc + '] 未知命令: ' + command);
  showHelp();
  process.exit(1);
}
