/** * ArenaSK Server — 主入口 * * 启动: node index.js | node --watch index.js (开发) */ const express = require('express'); const cors = require('cors'); const cookieParser = require('cookie-parser'); const path = require('path'); const http = require('http'); const WebSocket = require('ws'); const config = require('./config'); const { ensureDataDirs } = require('./services/skill-dir-manager'); const { PluginManager } = require('./src/plugins/plugin-manager'); const { initClaudeChatWSServer } = require('./routes/claude-chat-ws'); const app = express(); const server = http.createServer(app); const pluginManager = new PluginManager(); // ─── 中间件 ─── app.use(cors(config.cors)); app.use(cookieParser()); app.use(express.json()); // ─── API 路由 ─── app.use('/api/claude-chat', require('./routes/claude-chat')); app.use('/api/config', require('./routes/config')); app.use('/api/evolution', require('./routes/evolution')); app.use('/api/innovator', require('./routes/innovator')); app.use('/api/master', require('./routes/master-chat')); app.use('/api/open', require('./routes/open-api')); app.use('/api/arena', require('./routes/arena')); app.use('/api/auth', require('./routes/auth')); app.use('/api/whatsapp', require('./routes/whatsapp-auth')); app.use('/api/users', require('./routes/users')); app.use('/api/agent', require('./routes/agent-chat')); app.use('/api/goals', require('./routes/goals')); // ─── 系统端点 ─── app.get('/api/health', (req, res) => res.json({ status: 'ok', timestamp: new Date().toISOString(), plugins: pluginManager.summary(), })); app.get('/api/plugins', (req, res) => res.json(pluginManager.summary())); // ─── 静态文件 (生产环境) ─── if (process.env.NODE_ENV === 'production') { app.use(express.static(path.join(__dirname, '../web/dist'))); app.get('*', (req, res) => res.sendFile(path.join(__dirname, '../web/dist/index.html'))); } app.use((err, req, res, next) => { console.error('Server error:', err); res.status(500).json({ error: err.message }); }); // ─── 启动 ─── async function start() { try { ensureDataDirs(); const host = config.host || '0.0.0.0'; server.listen(Number(config.port), host, () => { console.log(`✅ 服务启动: http://${host}:${config.port}`); }); const wss = new WebSocket.Server({ server, path: '/ws/claude' }); initClaudeChatWSServer(wss); // 插件系统(异步,不阻塞 HTTP) initPlugins().catch(err => console.error('插件启动失败:', err.message)); // 恢复中断任务并起搏 Agent (延迟 5s,等 HTTP 服务完全就绪) setTimeout(() => { resumeIncompleteTasks().catch(err => console.error('[Startup] 恢复任务失败:', err.message)); const heartbeatService = require('./services/heartbeat-service'); heartbeatService.start(); }, 5000); } catch (error) { console.error('Failed to start server:', error); process.exit(1); } } async function resumeIncompleteTasks() { const taskMgr = require('./services/skill-task-manager'); const skillsDir = config.skillsCodeDir || path.resolve(__dirname, '../skills'); const incomplete = taskMgr.findIncompleteTasks(skillsDir); if (incomplete.length === 0) return console.log('[Startup] ✅ 无中断任务'); console.log(`[Startup] 🔄 恢复 ${incomplete.length} 个中断任务...`); const { _jobManager, _runSkillCreation, _runFeedbackIteration } = require('./routes/open-api'); for (const item of incomplete) { try { const job = _jobManager.createJob({ domain: item.task.domain || '', scope: item.task.scope || '', description: item.task.description || '', keywords: item.task.keywords || [], }); job.skillName = item.skillName; if (item.task.pipelineType === 'feedback' && _runFeedbackIteration) { await _runFeedbackIteration(job, item.task.feedback || '', { skillName: item.skillName, domain: item.task.domain || '', scope: item.task.scope || '', keywords: item.task.keywords || [], }); } else if (_runSkillCreation) { await _runSkillCreation(job, item.nextStep); } } catch (err) { console.error(`[Startup] 恢复 ${item.skillName} 失败:`, err.message); } } } async function initPlugins() { await pluginManager.init(); if (!pluginManager.enabled) return console.log('ℹ️ 插件: 未配置 channel,跳过'); const { runOrchestration, runMasterSession } = require('./src/standalone-entry'); await pluginManager.start({ runOrchestration: ({ task }) => runOrchestration(task), runMasterSession: ({ message }) => runMasterSession(message), }); console.log(`✅ 插件就绪: channels=[${pluginManager.channels.map(c => c.id)}], tools=${pluginManager.tools.length}`); } // ─── 优雅关闭 ─── async function shutdown() { console.log('\n正在关闭...'); await pluginManager.shutdown(); process.exit(0); } process.on('SIGTERM', shutdown); process.on('SIGINT', shutdown); start(); module.exports = app;