Files
telegram-bot/src/doctor.js
T

125 lines
3.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'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 };