Skill: yichuan007/post-upgrade-repair (version 1.0.0)

--- name: openclaw-repair-kit description: OpenClaw 升级后自动健康检查与修复工具。检测配置错误、飞书/Telegram 断线、记忆同步失败等问题，自动修复已知问题，未知问题联系 Claude AI 分析解决。 --- ## 用途 - 升级 OpenClaw 后自动检测并修复常见问题 - 调用 Claude AI 解答编程问题、分析内容、生成方案 ## 安装（一键） ```bash node install.mjs ``` 安装后每次重启 KKClaw 会自动检查，无需手动操作。 ## 手动运行健康检查 ```bash # Windows node "%USERPROFILE%\.openclaw\workspace\skills\openclaw-repair-kit\check.mjs" # Mac/Linux node ~/.openclaw/workspace/skills/openclaw-repair-kit/check.mjs ``` ## 手动调用 Claude AI ```bash # Windows node "%USERPROFILE%\.openclaw\workspace\skills\openclaw-repair-kit\run.mjs" "你的问题" # Mac/Linux node ~/.openclaw/workspace/skills/openclaw-repair-kit/run.mjs "你的问题" ``` ## 自动修复的已知问题 | 问题 | 原因 | |------|------| | 飞书私信全部被拦截 | `dmAllowlist` 改名为 `allowFrom` | | Telegram 配置失效 | `streamMode` 改名为 `streaming` | | 飞书 DM 策略无效 | `dmPolicy: "auto"` 已移除 | | 启动失败 | `model.image` 字段已移除 | | memory sync 报错 | OpenAI embeddings 国内无法访问 | ## 前提条件 - 已安装 OpenClaw（`npm install -g openclaw`） - openclaw.json 里配置了可用的 AI 模型 - Node.js 18 或以上版本

path: check.mjs
content: #!/usr/bin/env node
/**
 * OpenClaw 升级后健康检查脚本（可分享版本）
 * 自动检测常见问题，调用 AI 获取修复方案
 */

import { execSync } from 'child_process';
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { join, dirname } from 'path';
import { homedir } from 'os';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const HOME = homedir();
const OPENCLAW_WORKSPACE = join(HOME, '.openclaw', 'workspace');
const CC_SCRIPT = join(OPENCLAW_WORKSPACE, 'skills', 'openclaw-repair-kit', 'run.mjs');
const CONFIG_PATH = join(HOME, '.openclaw', 'openclaw.json');

// 自动修复已知的配置字段变更
function autoFixConfig() {
  const fixes = [];
  let config;
  try {
    config = JSON.parse(readFileSync(CONFIG_PATH, 'utf8'));
  } catch {
    return fixes;
  }

  let changed = false;
  const feishu = config?.channels?.feishu;

  // 修复1: dmAllowlist → allowFrom（2026.2.24）
  if (feishu?.dmAllowlist && !feishu?.allowFrom) {
    config.channels.feishu.allowFrom = feishu.dmAllowlist;
    delete config.channels.feishu.dmAllowlist;
    fixes.push('✅ 自动修复：飞书 dmAllowlist → allowFrom');
    changed = true;
  }

  // 修复2: streamMode → streaming（2026.2.24）
  const telegram = config?.channels?.telegram;
  if (telegram?.streamMode && !telegram?.streaming) {
    config.channels.telegram.streaming = telegram.streamMode;
    delete config.channels.telegram.streamMode;
    fixes.push('✅ 自动修复：Telegram streamMode → streaming');
    changed = true;
  }

  // 修复3: agents.defaults.model.image 字段移除
  if (config?.agents?.defaults?.model?.image) {
    delete config.agents.defaults.model.image;
    fixes.push('✅ 自动修复��移除 agents.defaults.model.image（新版不支持）');
    changed = true;
  }

  // 修复4: feishu dmPolicy "auto" 不合法
  if (feishu?.dmPolicy === 'auto') {
    config.channels.feishu.dmPolicy = 'allowlist';
    fixes.push('✅ 自动修复：飞书 dmPolicy "auto" → "allowlist"');
    changed = true;
  }

  if (changed) {
    writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf8');
  }

  return fixes;
}

// 自动检测 openclaw 命令
function getOpenclawCmd() {
  try {
    execSync('openclaw --version', { encoding: 'utf8', timeout: 5000, stdio: 'pipe' });
    return 'openclaw';
  } catch {
    // 尝试 npm 全局路径
    const npmPaths = [
      join(HOME, 'AppData', 'Roaming', 'npm', 'node_modules', 'openclaw', 'dist', 'index.js'),
      '/usr/local/lib/node_modules/openclaw/dist/index.js',
      '/usr/lib/node_modules/openclaw/dist/index.js',
    ];
    for (const p of npmPaths) {
      if (existsSync(p)) return `node "${p}"`;
    }
    return null;
  }
}

const openclawCmd = getOpenclawCmd();
if (!openclawCmd) {
  console.error('❌ 找不到 openclaw 命令，请确认 OpenClaw 已安装（npm install -g openclaw）');
  process.exit(1);
}

function run(cmd) {
  try {
    return execSync(`${openclawCmd} ${cmd}`, { encoding: 'utf8', timeout: 15000, stdio: 'pipe' });
  } catch (e) {
    return (e.stdout || '') + (e.stderr || '');
  }
}

console.log('🔍 正在检查 OpenClaw 健康状态...\n');

// 先尝试自动修复已知问题
const autoFixes = autoFixConfig();
if (autoFixes.length > 0) {
  console.log('🔧 自动修复已知配置问题：');
  autoFixes.forEach(f => console.log(' ', f));
  console.log('');
}

const issues = [];

// 1. 检查 config 是否有效
const version = run('--version');
if (version.includes('Invalid config')) {
  const match = version.match(/- (.+)/g);
  issues.push(`❌ 配置文件错误:\n${match ? match.join('\n') : version.trim()}`);
} else {
  console.log(`✅ 版本：${version.trim()}`);
}

// 2. 检查 channels 是否连接
const status = run('status');
const channelSection = status.match(/Channels[\s\S]*?Sessions/)?.[0] || '';
if (channelSection.includes('└──') && !channelSection.match(/│\s+\w+\s+│\s+(true|false)/)) {
  console.log('⚠️  Channels 表格为空。');
  console.log('');
  console.log('👉 【第一步】请先重启 KKClaw，然后重新运行此脚本。');
  console.log('   重启后如果飞书/Telegram 恢复正常，说明是重启生效了，无需继续。');
  console.log('   重启后仍然空白，说明配置有问题，脚本会自动联系 CC 修复。');
  console.log('');
  issues.push('❌ Channels 表格为空，飞书/Telegram 均未连接（如已重启仍未恢复，可能是升级后配置字段变更）');
} else {
  console.log('✅ 频道连接正常');
}

// 3. 检查 memory
const memStatus = run('memory');
if (memStatus.includes('fetch failed') || memStatus.includes('index failed')) {
  issues.push(`❌ 记忆同步失败（通常是 OpenAI embeddings 无法访问）:\n${memStatus.trim()}`);
} else {
  console.log('✅ 记忆状态正常');
}

// 4. 检查 doctor
const doctor = run('doctor');
if (doctor.includes('Config invalid') || doctor.includes('Invalid config') || doctor.includes('Unrecognized key')) {
  issues.push(`❌ Doctor 发现配置问题:\n${doctor.substring(0, 800)}`);
} else {
  console.log('✅ Doctor 检查通过');
}

console.log('');

if (issues.length === 0) {
  console.log('✅ 所有检查通过，OpenClaw 运行正常！');
  process.exit(0);
}

// 发现问题
console.log(`⚠️  发现 ${issues.length} 个问题：\n`);
issues.forEach(i => console.log(i + '\n'));

// 检查 CC 是否可用
if (!existsSync(CC_SCRIPT)) {
  console.log('💡 修复建议：');
  console.log('1. 确认 openclaw-repair-kit 已正确安装到：', join(OPENCLAW_WORKSPACE, 'skills', 'openclaw-repair-kit'));
  console.log('2. 重新运行 node install.mjs 安装');
  process.exit(1);
}

const configPath = join(HOME, '.openclaw', 'openclaw.json');
const problemReport = `OpenClaw 升级后自动检查发现以下问题，请帮我修复：

${issues.join('\n\n')}

当前版本：${version.trim()}

openclaw status 输出（节选）：
${status.substring(0, 1200)}

配置文件路径：${configPath}

请分析问题原因并给出具体的修复步骤。`;

console.log('🔧 正在联系 CC 获取修复方案...\n');

try {
  const result = execSync(
    `node "${CC_SCRIPT}" ${JSON.stringify(problemReport)}`,
    { encoding: 'utf8', timeout: 60000 }
  );
  console.log('📋 CC 的修复方案：\n');
  console.log(result);
} catch (e) {
  console.error('调用 CC 失败:', e.message);
  console.log('\n请手动将上方问题报告发给 CC（claude-code-agent）处理。');
}


path: install.mjs
content: #!/usr/bin/env node
/**
 * openclaw-repair-kit - 一键安装脚本
 * 运行方式: node install.mjs
 */

import { execSync } from 'child_process';
import { existsSync, mkdirSync, copyFileSync, writeFileSync } from 'fs';
import { join, dirname } from 'path';
import { homedir } from 'os';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const HOME = homedir();
const SKILLS_DIR = join(HOME, '.openclaw', 'workspace', 'skills');
const WORKSPACE_DIR = join(HOME, '.openclaw', 'workspace');

function run(cmd) {
  try {
    return execSync(cmd, { encoding: 'utf8', timeout: 15000, stdio: 'pipe' });
  } catch (e) {
    return (e.stdout || '') + (e.stderr || '');
  }
}

console.log('🦞 openclaw-repair-kit - 安装中...\n');

// 1. 检查 openclaw 是否安装
const version = run('openclaw --version');
if (!version.trim().match(/^\d{4}\.\d+\.\d+/)) {
  console.error('❌ 找不到 openclaw，请先安装：npm install -g openclaw');
  process.exit(1);
}
console.log(`✅ 检测到 OpenClaw ${version.trim()}`);

// 2. 检查 workspace 是否存在
if (!existsSync(WORKSPACE_DIR)) {
  console.error(`❌ 找不到 OpenClaw workspace: ${WORKSPACE_DIR}`);
  console.error('请确认 OpenClaw 已初始化（运行过 KKClaw 或 openclaw gateway）');
  process.exit(1);
}

// 3. 安装 openclaw-repair-kit skill
console.log('\n📦 安装 openclaw-repair-kit...');
const destDir = join(SKILLS_DIR, 'openclaw-repair-kit');
mkdirSync(destDir, { recursive: true });

const files = ['SKILL.md', 'check.mjs', 'run.mjs'].filter(f => existsSync(join(__dirname, f)));
files.forEach(f => {
  copyFileSync(join(__dirname, f), join(destDir, f));
  console.log(`  复制: ${f}`);
});
console.log('✅ openclaw-repair-kit 安装完成');

// 4. 创建/更新 BOOT.md
console.log('\n📝 配置启动自动检查 (BOOT.md)...');
const bootPath = join(WORKSPACE_DIR, 'BOOT.md');
const bootContent = `# BOOT — 每次网关启动时执行

## 升级后自动健康检查

启动时检查 OpenClaw 版本是否发生变化，若检测到升级，立即运行健康检查脚本修复常见问题。

执行以下步骤：

1. 读取当前运行版本：执行 \`system.run\` → \`openclaw --version\`，取第一行作为当前版本。
2. 读取上次记录的版本：读取文件 \`memory/last-version.txt\`（不存在则视为首次启动）。
3. 对比两个版本：
   - **相同** → 无需检查，正常启动。
   - **不同或文件不存在** → 检测到升级，执行步骤 4。
4. 运行健康检查：执行 \`system.run\` → \`node "skills/openclaw-repair-kit/check.mjs"\`
5. 将当前版本写入 \`memory/last-version.txt\`。
6. 不需要回复用户，静默完成即可（NO_REPLY）。
`;
writeFileSync(bootPath, bootContent, 'utf8');
console.log('✅ BOOT.md 配置完成');

// 5. 启用 boot-md hook
console.log('\n🔧 启用 boot-md hook...');
const hookResult = run('openclaw hooks enable boot-md');
if (hookResult.includes('enabled') || hookResult.includes('already')) {
  console.log('✅ boot-md hook 已启用');
} else {
  console.log('⚠️  请手动启用 hook：openclaw hooks enable boot-md');
  console.log('   详情：', hookResult.trim());
}

// 6. 写入当前版本（避免首次启动误触发）
const lastVersionPath = join(WORKSPACE_DIR, 'memory', 'last-version.txt');
mkdirSync(join(WORKSPACE_DIR, 'memory'), { recursive: true });
writeFileSync(lastVersionPath, version.trim(), 'utf8');
console.log('✅ 已记录当前版本');

console.log(`
✅ 安装完成！

工作原理：
  每次重启 KKClaw → boot-md hook 触发 → 比较版本号
  → 检测到升级 → 自动运行健康检查 → 发现问题自动联系 AI 修复

下次升级后重启 KKClaw，修复将全自动进行。

也可手动运行健康检查：
  node "${join(destDir, 'check.mjs')}"
`);


path: run.mjs
content: import { readFileSync } from 'fs';
import { join } from 'path';
import { homedir } from 'os';

const task = process.argv.slice(2).join(' ');
if (!task) {
  console.error('用法: node run.mjs "任务描述"');
  process.exit(1);
}

// 从用户自己的 openclaw.json 读取 API 凭证
const configPath = join(homedir(), '.openclaw', 'openclaw.json');
let config;
try {
  config = JSON.parse(readFileSync(configPath, 'utf8'));
} catch (e) {
  console.error('无法读取 openclaw.json:', e.message);
  console.error('请确认 OpenClaw 已安装且配置文件存在：', configPath);
  process.exit(1);
}

// 自动从主模型配置找到对应的 provider
const primaryModel = config?.agents?.defaults?.model?.primary || '';
const providerName = primaryModel.split('/')[0];
const modelId = primaryModel.split('/').slice(1).join('/');

const provider = config?.models?.providers?.[providerName];
if (!provider) {
  console.error(`找不到主模型 "${primaryModel}" 对应的 provider "${providerName}"`);
  console.error('请检查 openclaw.json 里的 agents.defaults.model.primary 配置');
  process.exit(1);
}

const [REDACTED_SECRET];
const baseUrl = provider.baseUrl.replace(/\/$/, '');

// 找到该 provider 下匹配的模型信息
const modelInfo = provider.models?.find(m => m.id === modelId);
const useAnthropicAPI = modelInfo?.api === 'anthropic-messages' || provider.api === 'anthropic-messages';

const response = await fetch(`${baseUrl}/messages`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': apiKey,
    'anthropic-version': '2023-06-01'
  },
  body: JSON.stringify({
    model: modelId,
    max_tokens: 8192,
    messages: [{ role: 'user', content: task }]
  })
});

const data = await response.json();
const textBlock = data.content?.find(c => c.type === 'text');
if (textBlock?.text) {
  process.stdout.write(textBlock.text);
} else {
  console.error('API 错误:', JSON.stringify(data));
  process.exit(1);
}


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