'use strict'; // 環境檢查:start 前確認 AI CLI(codex / 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 login(Codex 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 };