@iamsamyiok/agents-chat 3.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -0
- package/app/lib/agent.js +487 -0
- package/app/lib/aux-llm.js +107 -0
- package/app/lib/cards.js +479 -0
- package/app/lib/env.js +47 -0
- package/app/lib/memory.js +202 -0
- package/app/lib/oc.js +244 -0
- package/app/lib/orchestrator.js +1177 -0
- package/app/lib/store.js +686 -0
- package/app/mock/mock-agent.js +125 -0
- package/app/public/cards.html +628 -0
- package/app/public/index.html +3279 -0
- package/app/server.js +1334 -0
- package/bin/agents-chat.js +263 -0
- package/lib/start.js +10 -0
- package/package.json +39 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* agents-chat CLI 入口
|
|
4
|
+
*
|
|
5
|
+
* 用法:
|
|
6
|
+
* agents-chat # 启动服务(后台运行)
|
|
7
|
+
* agents-chat start # 同上
|
|
8
|
+
* agents-chat stop # 停止服务
|
|
9
|
+
* agents-chat status # 查看服务状态
|
|
10
|
+
* agents-chat open # 仅打开浏览器
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { spawn, execSync } = require('child_process');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const os = require('os');
|
|
17
|
+
|
|
18
|
+
const PORT = parseInt(process.env.AGENTS_CHAT_PORT || '3456', 10);
|
|
19
|
+
const PID_FILE = path.join(os.homedir(), '.agents-chat.pid');
|
|
20
|
+
const LOG_FILE = path.join(os.homedir(), '.agents-chat.log');
|
|
21
|
+
const DATA_DIR = process.env.AGENTS_CHAT_DATA || path.join(os.homedir(), '.agents-chat');
|
|
22
|
+
const SERVER_PATH = path.join(__dirname, '..', 'app', 'server.js');
|
|
23
|
+
|
|
24
|
+
// 确保数据目录存在
|
|
25
|
+
if (!fs.existsSync(DATA_DIR)) {
|
|
26
|
+
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isRunning() {
|
|
30
|
+
try {
|
|
31
|
+
const pid = fs.readFileSync(PID_FILE, 'utf8').trim();
|
|
32
|
+
if (pid) {
|
|
33
|
+
// 检查进程是否还在运行
|
|
34
|
+
try {
|
|
35
|
+
process.kill(parseInt(pid, 10), 0);
|
|
36
|
+
// 同时检查端口是否响应
|
|
37
|
+
try {
|
|
38
|
+
execSync(`curl -s -m 2 http://localhost:${PORT}/api/health`, { stdio: 'pipe' });
|
|
39
|
+
return { running: true, pid: parseInt(pid, 10) };
|
|
40
|
+
} catch {
|
|
41
|
+
// 端口无响应,进程可能已死
|
|
42
|
+
return { running: false, pid: parseInt(pid, 10), dead: true };
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
return { running: false, pid: null, dead: true };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
} catch {
|
|
49
|
+
// PID 文件不存在
|
|
50
|
+
}
|
|
51
|
+
return { running: false, pid: null };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function startServer() {
|
|
55
|
+
const check = isRunning();
|
|
56
|
+
if (check.running) {
|
|
57
|
+
console.log(`服务已在运行 (PID: ${check.pid})`);
|
|
58
|
+
console.log(`访问 http://localhost:${PORT}`);
|
|
59
|
+
openBrowser();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (check.dead) {
|
|
64
|
+
console.log('清理残留进程...');
|
|
65
|
+
cleanupDeadProcess(check.pid);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
console.log(`启动 Agents Chat 服务 (端口 ${PORT})...`);
|
|
69
|
+
|
|
70
|
+
const env = {
|
|
71
|
+
...process.env,
|
|
72
|
+
AGENTS_CHAT_DATA: DATA_DIR
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const options = {
|
|
76
|
+
cwd: path.join(__dirname, '..'),
|
|
77
|
+
env: env,
|
|
78
|
+
detached: true,
|
|
79
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
80
|
+
windowsHide: true
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Windows 特殊处理
|
|
84
|
+
if (process.platform === 'win32') {
|
|
85
|
+
options.shell = true;
|
|
86
|
+
options.windowsHide = true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const child = spawn(process.execPath, [SERVER_PATH, '--port', PORT], options);
|
|
90
|
+
|
|
91
|
+
child.on('error', (err) => {
|
|
92
|
+
console.error('启动失败:', err.message);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// 写入 PID 文件
|
|
97
|
+
if (child.pid) {
|
|
98
|
+
fs.writeFileSync(PID_FILE, String(child.pid));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 分离进程
|
|
102
|
+
child.unref();
|
|
103
|
+
|
|
104
|
+
// 等待服务就绪
|
|
105
|
+
console.log('等待服务就绪...');
|
|
106
|
+
let retries = 30;
|
|
107
|
+
const poll = setInterval(() => {
|
|
108
|
+
retries--;
|
|
109
|
+
try {
|
|
110
|
+
execSync(`curl -s -m 2 http://localhost:${PORT}/api/health`, { stdio: 'pipe' });
|
|
111
|
+
clearInterval(poll);
|
|
112
|
+
console.log('服务已就绪!');
|
|
113
|
+
console.log(`访问 http://localhost:${PORT}`);
|
|
114
|
+
openBrowser();
|
|
115
|
+
} catch {
|
|
116
|
+
if (retries <= 0) {
|
|
117
|
+
clearInterval(poll);
|
|
118
|
+
console.error('服务启动超时,请查看日志:');
|
|
119
|
+
console.log(` ${LOG_FILE}`);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}, 500);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function stopServer() {
|
|
127
|
+
const check = isRunning();
|
|
128
|
+
if (!check.running) {
|
|
129
|
+
console.log('服务未运行');
|
|
130
|
+
if (check.dead) {
|
|
131
|
+
cleanupDeadProcess(check.pid);
|
|
132
|
+
}
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
console.log(`停止服务 (PID: ${check.pid})...`);
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
// Windows 使用 taskkill 确保清理子进程
|
|
140
|
+
if (process.platform === 'win32') {
|
|
141
|
+
execSync(`taskkill /pid ${check.pid} /T /F`, { stdio: 'ignore' });
|
|
142
|
+
} else {
|
|
143
|
+
process.kill(check.pid, 'SIGTERM');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 等待进程退出
|
|
147
|
+
let retries = 10;
|
|
148
|
+
const wait = setInterval(() => {
|
|
149
|
+
retries--;
|
|
150
|
+
try {
|
|
151
|
+
process.kill(check.pid, 0);
|
|
152
|
+
if (retries <= 0) {
|
|
153
|
+
clearInterval(wait);
|
|
154
|
+
// 强制终止
|
|
155
|
+
try { process.kill(check.pid, 'SIGKILL'); } catch {}
|
|
156
|
+
}
|
|
157
|
+
} catch {
|
|
158
|
+
clearInterval(wait);
|
|
159
|
+
console.log('服务已停止');
|
|
160
|
+
}
|
|
161
|
+
}, 100);
|
|
162
|
+
} catch (err) {
|
|
163
|
+
console.error('停止失败:', err.message);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// 清理 PID 文件
|
|
167
|
+
try { fs.unlinkSync(PID_FILE); } catch {}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function cleanupDeadProcess(pid) {
|
|
171
|
+
try {
|
|
172
|
+
if (pid) {
|
|
173
|
+
if (process.platform === 'win32') {
|
|
174
|
+
execSync(`taskkill /pid ${pid} /T /F`, { stdio: 'ignore' });
|
|
175
|
+
} else {
|
|
176
|
+
try { process.kill(pid, 'SIGKILL'); } catch {}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
} catch {}
|
|
180
|
+
|
|
181
|
+
// 也通过命令行特征清理
|
|
182
|
+
try {
|
|
183
|
+
if (process.platform === 'win32') {
|
|
184
|
+
execSync('powershell -NoProfile -Command "Get-CimInstance Win32_Process | Where-Object { $_.Name -match \'node\' -and $_.CommandLine -like \'*app\\\\server.js*\' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }"', { stdio: 'ignore' });
|
|
185
|
+
}
|
|
186
|
+
} catch {}
|
|
187
|
+
|
|
188
|
+
try { fs.unlinkSync(PID_FILE); } catch {}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function openBrowser() {
|
|
192
|
+
const url = `http://localhost:${PORT}`;
|
|
193
|
+
try {
|
|
194
|
+
if (process.platform === 'win32') {
|
|
195
|
+
execSync(`start "" "${url}"`, { shell: true });
|
|
196
|
+
} else if (process.platform === 'darwin') {
|
|
197
|
+
execSync(`open "${url}"`);
|
|
198
|
+
} else {
|
|
199
|
+
execSync(`xdg-open "${url}"`);
|
|
200
|
+
}
|
|
201
|
+
} catch {
|
|
202
|
+
console.log(`请手动访问: ${url}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function showStatus() {
|
|
207
|
+
const check = isRunning();
|
|
208
|
+
if (check.running) {
|
|
209
|
+
console.log(`状态: 运行中 (PID: ${check.pid})`);
|
|
210
|
+
console.log(`端口: ${PORT}`);
|
|
211
|
+
console.log(`数据目录: ${DATA_DIR}`);
|
|
212
|
+
console.log(`日志文件: ${LOG_FILE}`);
|
|
213
|
+
} else {
|
|
214
|
+
console.log('状态: 未运行');
|
|
215
|
+
if (check.dead) {
|
|
216
|
+
console.log('发现残留进程,已清理');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// 主逻辑
|
|
222
|
+
const command = process.argv[2] || 'start';
|
|
223
|
+
|
|
224
|
+
switch (command) {
|
|
225
|
+
case 'start':
|
|
226
|
+
case '':
|
|
227
|
+
startServer();
|
|
228
|
+
break;
|
|
229
|
+
case 'stop':
|
|
230
|
+
stopServer();
|
|
231
|
+
break;
|
|
232
|
+
case 'status':
|
|
233
|
+
showStatus();
|
|
234
|
+
break;
|
|
235
|
+
case 'open':
|
|
236
|
+
openBrowser();
|
|
237
|
+
break;
|
|
238
|
+
case 'help':
|
|
239
|
+
case '--help':
|
|
240
|
+
case '-h':
|
|
241
|
+
console.log(`
|
|
242
|
+
Agents Chat CLI
|
|
243
|
+
|
|
244
|
+
用法:
|
|
245
|
+
agents-chat 启动服务(后台运行,自动打开浏览器)
|
|
246
|
+
agents-chat start 同上
|
|
247
|
+
agents-chat stop 停止服务
|
|
248
|
+
agents-chat status 查看服务状态
|
|
249
|
+
agents-chat open 仅打开浏览器
|
|
250
|
+
|
|
251
|
+
环境变量:
|
|
252
|
+
AGENTS_CHAT_PORT 端口号 (默认: 3456)
|
|
253
|
+
AGENTS_CHAT_DATA 数据目录 (默认: ~/.agents-chat)
|
|
254
|
+
|
|
255
|
+
配置:
|
|
256
|
+
编辑 ~/.agents-chat/.env 文件进行配置
|
|
257
|
+
`);
|
|
258
|
+
break;
|
|
259
|
+
default:
|
|
260
|
+
console.error(`未知命令: ${command}`);
|
|
261
|
+
console.log('运行 agents-chat help 查看帮助');
|
|
262
|
+
process.exit(1);
|
|
263
|
+
}
|
package/lib/start.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@iamsamyiok/agents-chat",
|
|
3
|
+
"version": "3.18.0",
|
|
4
|
+
"description": "多智能体群聊工具 - 支持 OpenCode/Claude Code/Codex/pi 内核,微信风格聊天界面",
|
|
5
|
+
"main": "lib/start.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agents-chat": "./bin/agents-chat.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"lib",
|
|
12
|
+
"app"
|
|
13
|
+
],
|
|
14
|
+
"keywords": [
|
|
15
|
+
"ai",
|
|
16
|
+
"agent",
|
|
17
|
+
"chat",
|
|
18
|
+
"multi-agent",
|
|
19
|
+
"opencode",
|
|
20
|
+
"claude",
|
|
21
|
+
"codex"
|
|
22
|
+
],
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18.0.0"
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/iamsamyiok/agents-chat"
|
|
30
|
+
},
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/iamsamyiok/agents-chat/issues"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/iamsamyiok/agents-chat",
|
|
35
|
+
"scripts": {
|
|
36
|
+
"start": "node app/server.js",
|
|
37
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
38
|
+
}
|
|
39
|
+
}
|