index.js
5.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/**
* 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.use('/api/company', require('./routes/company'));
// ─── 系统端点 ───
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;