Files
telegram-bot/src/doctor.js
T

125 lines
3.4 KiB
JavaScript
Raw Normal View History

'use strict';
// 環境檢查:start 前確認 AI CLIcodex / claude 至少一個)、pm2、git 等工具是否就緒。
const { execSync } = require('child_process');
const fs = require('fs');
const os = require('os');
const path = require('path');
function tryExec(cmd) {
try {
return execSync(cmd, { encoding: 'utf-8', windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'], timeout: 15000 }).trim();
} catch {
return null;
}
}
function codexVersion() {
return tryExec('codex --version');
}
function claudeVersion() {
const { resolveClaudeCommand } = require('./engines/claude');
const { cmd } = resolveClaudeCommand({});
const out = tryExec(`"${cmd}" --version`) || tryExec('claude --version');
return out ? out.split('\n')[0] : null;
}
function claudeLoggedIn() {
if (fs.existsSync(path.join(os.homedir(), '.claude', '.credentials.json'))) return true;
try {
const j = JSON.parse(fs.readFileSync(path.join(os.homedir(), '.claude.json'), 'utf-8'));
return !!j.oauthAccount;
} catch {
return false;
}
}
// 回傳 [{ name, ok, required, message }]
function runChecks() {
const checks = [];
const nodeMajor = Number(process.versions.node.split('.')[0]);
checks.push({
name: 'Node.js',
ok: nodeMajor >= 18,
required: true,
message: nodeMajor >= 18 ? process.version : `${process.version}(需要 ≥ 18`,
});
const codexVer = codexVersion();
checks.push({
name: 'Codex CLI',
ok: !!codexVer,
required: false,
group: 'engine',
message: codexVer || '找不到 codex(要用 Codex 引擎才需要):npm install -g @openai/codex',
});
if (codexVer) {
const loggedIn = fs.existsSync(path.join(os.homedir(), '.codex', 'auth.json'));
checks.push({
name: 'Codex 登入',
ok: loggedIn,
required: false,
message: loggedIn ? '已登入' : '尚未登入,請先執行:codex loginCodex bot 啟動後才需要)',
});
}
const claudeVer = claudeVersion();
checks.push({
name: 'Claude Code CLI',
ok: !!claudeVer,
required: false,
group: 'engine',
message: claudeVer || '找不到 claude(要用 Claude 引擎才需要):npm install -g @anthropic-ai/claude-code',
});
if (claudeVer) {
const loggedIn = claudeLoggedIn();
checks.push({
name: 'Claude 登入',
ok: loggedIn,
required: false,
message: loggedIn ? '已登入' : '尚未登入,請先執行:claude(首次啟動會引導登入)',
});
}
if (!codexVer && !claudeVer) {
checks.push({
name: 'AI 引擎',
ok: false,
required: true,
message: 'codex 與 claude 都找不到,至少要裝一個',
});
}
const pm2Ver = tryExec('pm2 -v');
checks.push({
name: 'pm2',
ok: !!pm2Ver,
required: true,
message: pm2Ver ? `v${pm2Ver.split('\n').pop()}` : '找不到 pm2,請安裝:npm install -g pm2',
});
const gitVer = tryExec('git --version');
checks.push({
name: 'git',
ok: !!gitVer,
required: false,
message: gitVer || '找不到 git(非必要:AI 要做版控操作時會需要)',
});
return checks;
}
function printChecks(checks) {
for (const c of checks) {
const icon = c.ok ? '✅' : c.required ? '❌' : '⚠️';
console.log(`${icon} ${c.name}${c.message}`);
}
}
function hasBlocker(checks) {
return checks.some((c) => c.required && !c.ok);
}
module.exports = { runChecks, printChecks, hasBlocker, codexVersion, claudeVersion };