@neomei/agent-soul-framework 4.5.2 → 4.5.4
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 +30 -19
- package/connectors/feishu/background.sh +2 -2
- package/connectors/feishu/core-start.sh +20 -10
- package/connectors/feishu/hooks/on-session-created.sh +1 -1
- package/connectors/feishu/hooks/on-session-idle.sh +1 -1
- package/connectors/feishu/model-failover.sh +15 -3
- package/connectors/feishu/restart-all.sh +5 -4
- package/connectors/feishu/restart-feishu.sh +1 -1
- package/connectors/feishu/restart-serve.sh +1 -1
- package/connectors/feishu/scripts/session-cleanup.sh +1 -1
- package/connectors/feishu/start.sh +1 -1
- package/connectors/feishu/stop.sh +3 -4
- package/connectors/feishu/systemd/install-systemd.sh +11 -2
- package/connectors/feishu/systemd/sleep-hooks/99-hunqi-resume.sh +1 -1
- package/connectors/feishu/watchdog.sh +1 -1
- package/dist/cli/hunqi.d.ts +1 -1
- package/dist/cli/hunqi.js +112 -200
- package/dist/cli/hunqi.js.map +1 -1
- package/heartbeat_wrapper.sh +1 -1
- package/hunqi.sh +1 -1
- package/install.sh +13 -280
- package/package.json +4 -4
- package/scripts/content-filter.js +3 -1
- package/scripts/health-check.sh +2 -2
- package/scripts/session-cleanup.sh +1 -1
- package/start-feishu-daemon.sh +2 -2
- package/start.sh +1 -1
- package/test.sh +1 -1
- package/uninstall.sh +13 -2
- package/verify.sh +1 -1
- package/setup-wizard.sh +0 -420
- package/setup.sh +0 -128
package/dist/cli/hunqi.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 魂器 CLI — @neomei/agent-soul-framework
|
|
3
3
|
*
|
|
4
|
-
* 用法:
|
|
4
|
+
* 用法: agent-soul-framework <command> [options]
|
|
5
5
|
*/
|
|
6
|
-
import { existsSync, readFileSync, mkdirSync, copyFileSync, writeFileSync
|
|
7
|
-
import { dirname } from 'node:path';
|
|
6
|
+
import { existsSync, readFileSync, mkdirSync, copyFileSync, writeFileSync } from 'node:fs';
|
|
8
7
|
import { join } from 'node:path';
|
|
9
8
|
const PACKAGE_ROOT = join(import.meta.dirname, '..', '..');
|
|
10
9
|
function loadJSON(filepath) {
|
|
@@ -19,7 +18,7 @@ function findSkillsPackage() {
|
|
|
19
18
|
const candidates = [
|
|
20
19
|
join(PACKAGE_ROOT, '..', 'agent-soul-skills'),
|
|
21
20
|
join(PACKAGE_ROOT, 'node_modules', '@neomei', 'agent-soul-skills'),
|
|
22
|
-
join(process.env.HOME || '/', '.config', '
|
|
21
|
+
join(process.env.HOME || '/', '.config', 'agent-soul-framework', 'skills'),
|
|
23
22
|
];
|
|
24
23
|
for (const root of candidates) {
|
|
25
24
|
if (existsSync(join(root, 'skills', 'skill-creator', 'scripts', 'skill_creator.py')))
|
|
@@ -69,7 +68,7 @@ async function cmdHeartbeat() {
|
|
|
69
68
|
}
|
|
70
69
|
async function cmdSearch(query) {
|
|
71
70
|
if (!query) {
|
|
72
|
-
console.log('用法:
|
|
71
|
+
console.log('用法: agent-soul-framework search <关键词>');
|
|
73
72
|
return;
|
|
74
73
|
}
|
|
75
74
|
try {
|
|
@@ -101,7 +100,7 @@ async function cmdMemory(args) {
|
|
|
101
100
|
}
|
|
102
101
|
else if (sub === 'add') {
|
|
103
102
|
if (!args[1]) {
|
|
104
|
-
console.log('用法:
|
|
103
|
+
console.log('用法: agent-soul-framework memory add "内容"');
|
|
105
104
|
}
|
|
106
105
|
else {
|
|
107
106
|
mem.addMemory?.(args[1]);
|
|
@@ -110,7 +109,7 @@ async function cmdMemory(args) {
|
|
|
110
109
|
}
|
|
111
110
|
else if (sub === 'search') {
|
|
112
111
|
if (!args[1]) {
|
|
113
|
-
console.log('用法:
|
|
112
|
+
console.log('用法: agent-soul-framework memory search "关键词"');
|
|
114
113
|
}
|
|
115
114
|
else {
|
|
116
115
|
const hits = mem.search(args[1], 10) || [];
|
|
@@ -120,7 +119,7 @@ async function cmdMemory(args) {
|
|
|
120
119
|
}
|
|
121
120
|
}
|
|
122
121
|
else {
|
|
123
|
-
console.log('用法:
|
|
122
|
+
console.log('用法: agent-soul-framework memory <status|add|search>');
|
|
124
123
|
}
|
|
125
124
|
mem.close();
|
|
126
125
|
}
|
|
@@ -140,7 +139,7 @@ async function cmdKnowledge(args) {
|
|
|
140
139
|
generateIndex();
|
|
141
140
|
}
|
|
142
141
|
else {
|
|
143
|
-
console.log('用法:
|
|
142
|
+
console.log('用法: agent-soul-framework knowledge <daily|index>');
|
|
144
143
|
}
|
|
145
144
|
}
|
|
146
145
|
catch (e) {
|
|
@@ -168,153 +167,14 @@ async function cmdSkillCreate(args) {
|
|
|
168
167
|
async function cmdInteractive() {
|
|
169
168
|
try {
|
|
170
169
|
const { execSync } = await import('node:child_process');
|
|
171
|
-
const script = join(PACKAGE_ROOT, '
|
|
170
|
+
const script = join(PACKAGE_ROOT, 'agent-soul-framework.sh');
|
|
172
171
|
if (existsSync(script)) {
|
|
173
172
|
execSync('bash ' + script + ' interactive', { cwd: process.cwd(), stdio: 'inherit' });
|
|
174
173
|
return;
|
|
175
174
|
}
|
|
176
175
|
}
|
|
177
176
|
catch { }
|
|
178
|
-
console.log('请在魂器项目目录下运行: cd agent-soul-framework && ./
|
|
179
|
-
}
|
|
180
|
-
async function cmdInit(dirName) {
|
|
181
|
-
if (!dirName) {
|
|
182
|
-
console.log('用法: hunqi init <项目目录名>');
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
const targetDir = join(process.cwd(), dirName);
|
|
186
|
-
const dirs = [
|
|
187
|
-
'soul', 'skills', 'knowledge', 'memory/short-term', 'memory/long-term',
|
|
188
|
-
'memory/vector', 'heartbeat', 'scripts', 'connectors/feishu', 'connectors/moltbook',
|
|
189
|
-
'.opencode', 'config', 'logs', 'memory/cron-output', 'memory/.locks', 'memory/.queue',
|
|
190
|
-
'plugin',
|
|
191
|
-
];
|
|
192
|
-
for (const d of dirs)
|
|
193
|
-
mkdirSync(join(targetDir, d), { recursive: true });
|
|
194
|
-
const copyPairs = [
|
|
195
|
-
['soul/SOUL.md.example', 'soul/SOUL.md'],
|
|
196
|
-
['soul/IDENTITY.md.example', 'soul/IDENTITY.md'],
|
|
197
|
-
['soul/USER.md.example', 'soul/USER.md'],
|
|
198
|
-
['soul/HEARTBEAT.md.example', 'soul/HEARTBEAT.md'],
|
|
199
|
-
['.opencode/opencode.json.example', '.opencode/opencode.json'],
|
|
200
|
-
['.opencode/prompt.md.example', '.opencode/prompt.md'],
|
|
201
|
-
['.opencode/tools/read-plugin.js', '.opencode/tools/read-plugin.js'],
|
|
202
|
-
['plugin/index.js', 'plugin/index.js'],
|
|
203
|
-
['plugin/manifest.json', 'plugin/manifest.json'],
|
|
204
|
-
['plugin/package.json', 'plugin/package.json'],
|
|
205
|
-
['memory/MEMORY.md.example', 'memory/MEMORY.md'],
|
|
206
|
-
['AGENTS.md.example', 'AGENTS.md'],
|
|
207
|
-
['TOOLS.md.example', 'TOOLS.md'],
|
|
208
|
-
['heartbeat/heartbeat_tasks.json', 'heartbeat/heartbeat_tasks.json'],
|
|
209
|
-
];
|
|
210
|
-
for (const [src, dst] of copyPairs) {
|
|
211
|
-
const srcPath = join(PACKAGE_ROOT, src);
|
|
212
|
-
const dstPath = join(targetDir, dst);
|
|
213
|
-
if (existsSync(srcPath)) {
|
|
214
|
-
mkdirSync(dirname(dstPath), { recursive: true });
|
|
215
|
-
copyFileSync(srcPath, dstPath);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
const skillsRoot = findSkillsPackage();
|
|
219
|
-
if (skillsRoot) {
|
|
220
|
-
const skillsDir = join(skillsRoot, 'skills');
|
|
221
|
-
if (existsSync(skillsDir)) {
|
|
222
|
-
for (const skill of readdirSync(skillsDir)) {
|
|
223
|
-
const srcSkill = join(skillsDir, skill);
|
|
224
|
-
if (!existsSync(join(srcSkill, 'SKILL.md')))
|
|
225
|
-
continue;
|
|
226
|
-
const dstSkill = join(targetDir, 'skills', skill);
|
|
227
|
-
mkdirSync(dstSkill, { recursive: true });
|
|
228
|
-
const subs = readdirSync(srcSkill, { withFileTypes: true });
|
|
229
|
-
for (const s of subs) {
|
|
230
|
-
if (s.isFile()) {
|
|
231
|
-
copyFileSync(join(srcSkill, s.name), join(dstSkill, s.name));
|
|
232
|
-
}
|
|
233
|
-
else if (s.isDirectory() && s.name === 'scripts') {
|
|
234
|
-
mkdirSync(join(dstSkill, s.name), { recursive: true });
|
|
235
|
-
for (const ss of readdirSync(join(srcSkill, s.name), { withFileTypes: true })) {
|
|
236
|
-
if (ss.isFile()) {
|
|
237
|
-
copyFileSync(join(srcSkill, s.name, ss.name), join(dstSkill, s.name, ss.name));
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
const knowledgeDir = join(PACKAGE_ROOT, 'knowledge');
|
|
246
|
-
if (existsSync(knowledgeDir)) {
|
|
247
|
-
for (const item of readdirSync(knowledgeDir)) {
|
|
248
|
-
const srcPath = join(knowledgeDir, item);
|
|
249
|
-
if (!existsSync(srcPath) || item.startsWith('.'))
|
|
250
|
-
continue;
|
|
251
|
-
if (item.endsWith('.example')) {
|
|
252
|
-
const dstPath = join(targetDir, 'knowledge', item.replace('.example', ''));
|
|
253
|
-
copyFileSync(srcPath, dstPath);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
const cats = ['body', 'emotion', 'evolution', 'growth', 'intimacy', 'methodology', 'philosophy', 'system'];
|
|
257
|
-
for (const cat of cats) {
|
|
258
|
-
const catDir = join(knowledgeDir, cat);
|
|
259
|
-
if (!existsSync(catDir))
|
|
260
|
-
continue;
|
|
261
|
-
mkdirSync(join(targetDir, 'knowledge', cat), { recursive: true });
|
|
262
|
-
for (const item of readdirSync(catDir)) {
|
|
263
|
-
if (item.endsWith('.example')) {
|
|
264
|
-
const srcPath = join(catDir, item);
|
|
265
|
-
const dstPath = join(targetDir, 'knowledge', cat, item.replace('.example', ''));
|
|
266
|
-
copyFileSync(srcPath, dstPath);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
writeFileSync(join(targetDir, '.env'), '# 魂器环境配置\n' +
|
|
272
|
-
'# 编辑以下内容填入你的 API Key\n\n' +
|
|
273
|
-
'DASHSCOPE_API_KEY=\n' +
|
|
274
|
-
'FEISHU_APP_ID=\n' +
|
|
275
|
-
'FEISHU_APP_SECRET=\n' +
|
|
276
|
-
'MOLTBOOK_API_KEY=\n' +
|
|
277
|
-
'WECHAT_APP_ID=\n' +
|
|
278
|
-
'WECHAT_APP_SECRET=\n' +
|
|
279
|
-
'JIMENG_API_KEY=\n');
|
|
280
|
-
console.log('\n ✅ 项目 "' + dirName + '" 已创建');
|
|
281
|
-
if (process.argv.includes('--setup-cron')) {
|
|
282
|
-
try {
|
|
283
|
-
const { execSync } = await import('node:child_process');
|
|
284
|
-
const cronLine = '*/30 * * * * cd ' + targetDir + ' && ./heartbeat_wrapper.sh';
|
|
285
|
-
const existing = execSync('crontab -l 2>/dev/null || echo ""', { encoding: 'utf-8' });
|
|
286
|
-
if (!existing.includes('heartbeat_wrapper.sh')) {
|
|
287
|
-
const newCron = existing.trim() + (existing.trim() ? '\n' : '') + cronLine + '\n';
|
|
288
|
-
execSync('printf "%s" "$1" | crontab -', { encoding: 'utf-8', env: { ...process.env, '1': newCron } });
|
|
289
|
-
console.log(' 📅 crontab 已配置(每 30 分钟执行心跳)');
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
catch { }
|
|
293
|
-
}
|
|
294
|
-
try {
|
|
295
|
-
const { execSync } = await import('node:child_process');
|
|
296
|
-
if (existsSync(join(targetDir, 'node_modules', '.bin', 'hunqi-heartbeat'))) {
|
|
297
|
-
execSync(join(targetDir, 'node_modules', '.bin', 'hunqi-heartbeat'), {
|
|
298
|
-
cwd: targetDir, stdio: 'pipe', timeout: 120000, encoding: 'utf-8'
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
else {
|
|
302
|
-
execSync('hunqi-heartbeat', { cwd: targetDir, stdio: 'pipe', timeout: 120000, encoding: 'utf-8' });
|
|
303
|
-
}
|
|
304
|
-
console.log(' 💓 首次心跳已执行(记忆系统初始化完成)');
|
|
305
|
-
}
|
|
306
|
-
catch { }
|
|
307
|
-
console.log('\n 下一步:');
|
|
308
|
-
console.log(' cd ' + dirName);
|
|
309
|
-
console.log(' hunqi interactive # 启动交互式 TUI');
|
|
310
|
-
console.log(' hunqi status # 查看系统状态');
|
|
311
|
-
if (!process.argv.includes('--setup-cron')) {
|
|
312
|
-
const cronCmd = '*/30 * * * * cd ' + targetDir + ' && ./heartbeat_wrapper.sh';
|
|
313
|
-
console.log('\n 💡 配置心跳(记忆自动同步):');
|
|
314
|
-
console.log(' hunqi init --setup-cron # 下次初始化时加上此参数');
|
|
315
|
-
console.log(' 或 crontab -e 添加: ' + cronCmd);
|
|
316
|
-
}
|
|
317
|
-
console.log();
|
|
177
|
+
console.log('请在魂器项目目录下运行: cd agent-soul-framework && ./agent-soul-framework.sh interactive');
|
|
318
178
|
}
|
|
319
179
|
async function cmdConfig() {
|
|
320
180
|
const cfgPath = join(process.cwd(), '.opencode', 'opencode.json');
|
|
@@ -327,7 +187,7 @@ async function cmdDoctor() {
|
|
|
327
187
|
const cwd = process.cwd();
|
|
328
188
|
let checkDir = cwd;
|
|
329
189
|
if (!existsSync(join(checkDir, 'soul', 'SOUL.md'))) {
|
|
330
|
-
const defaultDir = join(process.env.HOME || '~', '.
|
|
190
|
+
const defaultDir = join(process.env.HOME || '~', '.agent-soul-framework');
|
|
331
191
|
if (existsSync(join(defaultDir, 'soul', 'SOUL.md'))) {
|
|
332
192
|
checkDir = defaultDir;
|
|
333
193
|
}
|
|
@@ -389,7 +249,7 @@ async function cmdDoctor() {
|
|
|
389
249
|
const hasMemory = existsSync(join(checkDir, 'memory', 'MEMORY.md'));
|
|
390
250
|
const hasDb = existsSync(join(checkDir, 'memory', 'short-term', 'conversations.db'));
|
|
391
251
|
console.log(' 项目目录: ' + checkDir);
|
|
392
|
-
console.log(' 项目灵魂: ' + (hasSoul ? '✅' : '⚠️ 未初始化,运行
|
|
252
|
+
console.log(' 项目灵魂: ' + (hasSoul ? '✅' : '⚠️ 未初始化,运行 agent-soul-framework start'));
|
|
393
253
|
console.log(' 记忆系统: ' + (hasMemory ? '✅' : '⚠️ ') + 'MEMORY.md | ' + (hasDb ? '✅' : '⚠️ ') + 'conversations.db');
|
|
394
254
|
if (hasDb) {
|
|
395
255
|
try {
|
|
@@ -411,7 +271,7 @@ async function cmdDoctor() {
|
|
|
411
271
|
console.log(' 心跳调度: crontab 已配置 ✅');
|
|
412
272
|
}
|
|
413
273
|
else {
|
|
414
|
-
console.log(' 心跳调度: 未配置 ⚠️ (运行
|
|
274
|
+
console.log(' 心跳调度: 未配置 ⚠️ (运行 agent-soul-framework start 自动配置)');
|
|
415
275
|
}
|
|
416
276
|
}
|
|
417
277
|
catch {
|
|
@@ -426,19 +286,19 @@ async function cmdDoctor() {
|
|
|
426
286
|
else {
|
|
427
287
|
console.log(' 环境变量: .env 不存在 ⚠️ (cp .env.example .env)');
|
|
428
288
|
}
|
|
429
|
-
console.log('\n 💡 一键修复:
|
|
289
|
+
console.log('\n 💡 一键修复: agent-soul-framework start\n');
|
|
430
290
|
}
|
|
431
291
|
async function cmdSetup() {
|
|
432
292
|
let cwd = process.cwd();
|
|
433
293
|
if (!existsSync(join(cwd, 'soul', 'SOUL.md'))) {
|
|
434
|
-
const defaultDir = join(process.env.HOME || '~', '.
|
|
294
|
+
const defaultDir = join(process.env.HOME || '~', '.agent-soul-framework');
|
|
435
295
|
if (existsSync(join(defaultDir, 'soul', 'SOUL.md'))) {
|
|
436
296
|
cwd = defaultDir;
|
|
437
297
|
}
|
|
438
298
|
}
|
|
439
299
|
console.log('\n 🔧 魂器自动配置\n ' + '─'.repeat(50));
|
|
440
300
|
if (!existsSync(join(cwd, 'soul')) && !existsSync(join(cwd, 'soul', 'SOUL.md'))) {
|
|
441
|
-
console.log(' ⚠️ 不在魂器项目目录,请先
|
|
301
|
+
console.log(' ⚠️ 不在魂器项目目录,请先 agent-soul-framework setup');
|
|
442
302
|
return;
|
|
443
303
|
}
|
|
444
304
|
let copied = 0;
|
|
@@ -476,11 +336,11 @@ async function cmdSetup() {
|
|
|
476
336
|
console.log(' 📝 模板初始化: 复制了 ' + copied + ' 个配置模板');
|
|
477
337
|
try {
|
|
478
338
|
const { execSync } = await import('node:child_process');
|
|
479
|
-
if (existsSync(join(cwd, 'node_modules', '.bin', '
|
|
480
|
-
execSync(join(cwd, 'node_modules', '.bin', '
|
|
339
|
+
if (existsSync(join(cwd, 'node_modules', '.bin', 'agent-soul-heartbeat'))) {
|
|
340
|
+
execSync(join(cwd, 'node_modules', '.bin', 'agent-soul-heartbeat'), { cwd, stdio: 'pipe', timeout: 120000, encoding: 'utf-8' });
|
|
481
341
|
}
|
|
482
342
|
else {
|
|
483
|
-
execSync('
|
|
343
|
+
execSync('agent-soul-heartbeat', { cwd, stdio: 'pipe', timeout: 120000, encoding: 'utf-8' });
|
|
484
344
|
}
|
|
485
345
|
console.log(' 💓 记忆系统: 已初始化');
|
|
486
346
|
}
|
|
@@ -511,6 +371,37 @@ async function cmdSetup() {
|
|
|
511
371
|
const { execSync } = await import('node:child_process');
|
|
512
372
|
execSync('opencode-feishu setup', { stdio: 'inherit', timeout: 300000 });
|
|
513
373
|
console.log('\n ✅ 飞书配置完成');
|
|
374
|
+
// 注入灵魂 hooks 到 feishu.json
|
|
375
|
+
try {
|
|
376
|
+
const hooksDir = join(cwd, 'connectors', 'feishu', 'hooks');
|
|
377
|
+
const srcHooksDir = join(PACKAGE_ROOT, 'connectors', 'feishu', 'hooks');
|
|
378
|
+
if (existsSync(srcHooksDir)) {
|
|
379
|
+
if (!existsSync(hooksDir))
|
|
380
|
+
mkdirSync(hooksDir, { recursive: true });
|
|
381
|
+
for (const f of ['on-session-created.sh', 'on-session-idle.sh']) {
|
|
382
|
+
const src = join(srcHooksDir, f);
|
|
383
|
+
const dst = join(hooksDir, f);
|
|
384
|
+
if (existsSync(src) && !existsSync(dst)) {
|
|
385
|
+
copyFileSync(src, dst);
|
|
386
|
+
try {
|
|
387
|
+
execSync('chmod +x ' + dst, { stdio: 'ignore', timeout: 3000 });
|
|
388
|
+
}
|
|
389
|
+
catch { }
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
const feishuRaw = readFileSync(feishuConfig, 'utf-8');
|
|
394
|
+
const feishuJson = JSON.parse(feishuRaw);
|
|
395
|
+
feishuJson.hooks = {
|
|
396
|
+
onSessionCreated: 'connectors/feishu/hooks/on-session-created.sh',
|
|
397
|
+
onSessionIdle: 'connectors/feishu/hooks/on-session-idle.sh',
|
|
398
|
+
};
|
|
399
|
+
writeFileSync(feishuConfig, JSON.stringify(feishuJson, null, 2));
|
|
400
|
+
console.log(' 🪝 灵魂 hooks: 已注入 feishu.json ✅');
|
|
401
|
+
}
|
|
402
|
+
catch (hookErr) {
|
|
403
|
+
console.log(' 🪝 灵魂 hooks: 注入失败 (' + (hookErr.message || 'unknown') + ')');
|
|
404
|
+
}
|
|
514
405
|
}
|
|
515
406
|
catch {
|
|
516
407
|
console.log('\n ⚠️ 飞书配置跳过(手动配置: opencode-feishu setup)');
|
|
@@ -518,6 +409,38 @@ async function cmdSetup() {
|
|
|
518
409
|
}
|
|
519
410
|
else {
|
|
520
411
|
console.log(' 📱 飞书连接: 已配置');
|
|
412
|
+
// 补充 hooks 配置(如果缺失)
|
|
413
|
+
try {
|
|
414
|
+
const { execSync: esc2 } = await import('node:child_process');
|
|
415
|
+
const feishuRaw = readFileSync(feishuConfig, 'utf-8');
|
|
416
|
+
const feishuJson = JSON.parse(feishuRaw);
|
|
417
|
+
if (!feishuJson.hooks || !feishuJson.hooks.onSessionCreated) {
|
|
418
|
+
const hooksDir = join(cwd, 'connectors', 'feishu', 'hooks');
|
|
419
|
+
const srcHooksDir = join(PACKAGE_ROOT, 'connectors', 'feishu', 'hooks');
|
|
420
|
+
if (existsSync(srcHooksDir)) {
|
|
421
|
+
if (!existsSync(hooksDir))
|
|
422
|
+
mkdirSync(hooksDir, { recursive: true });
|
|
423
|
+
for (const f of ['on-session-created.sh', 'on-session-idle.sh']) {
|
|
424
|
+
const src = join(srcHooksDir, f);
|
|
425
|
+
const dst = join(hooksDir, f);
|
|
426
|
+
if (existsSync(src) && !existsSync(dst)) {
|
|
427
|
+
copyFileSync(src, dst);
|
|
428
|
+
try {
|
|
429
|
+
esc2('chmod +x ' + dst, { stdio: 'ignore', timeout: 3000 });
|
|
430
|
+
}
|
|
431
|
+
catch { }
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
feishuJson.hooks = {
|
|
436
|
+
onSessionCreated: 'connectors/feishu/hooks/on-session-created.sh',
|
|
437
|
+
onSessionIdle: 'connectors/feishu/hooks/on-session-idle.sh',
|
|
438
|
+
};
|
|
439
|
+
writeFileSync(feishuConfig, JSON.stringify(feishuJson, null, 2));
|
|
440
|
+
console.log(' 🪝 灵魂 hooks: 已注入 feishu.json ✅');
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
catch { }
|
|
521
444
|
try {
|
|
522
445
|
const { execSync } = await import('node:child_process');
|
|
523
446
|
const status = execSync('opencode-feishu status 2>/dev/null || echo "stopped"', { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
@@ -576,7 +499,7 @@ async function cmdSetup() {
|
|
|
576
499
|
console.log(' 📱 飞书连接 — 配置文件就绪');
|
|
577
500
|
if (existsSync(qiweiConfig))
|
|
578
501
|
console.log(' 💬 企微连接 — 配置文件就绪');
|
|
579
|
-
console.log('\n 如需检查状态:
|
|
502
|
+
console.log('\n 如需检查状态: agent-soul-framework doctor');
|
|
580
503
|
}
|
|
581
504
|
async function askYN(prompt) {
|
|
582
505
|
const { createInterface } = await import('node:readline');
|
|
@@ -586,28 +509,20 @@ async function askYN(prompt) {
|
|
|
586
509
|
});
|
|
587
510
|
return answer === '' || answer === 'y' || answer === 'yes';
|
|
588
511
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
const { execSync } = await import('node:child_process');
|
|
593
|
-
execSync('opencode-feishu setup', { stdio: 'inherit', timeout: 300000 });
|
|
594
|
-
console.log(' ✅ 飞书配置完成\n');
|
|
595
|
-
}
|
|
596
|
-
catch {
|
|
597
|
-
console.log(' ⚠️ 飞书配置跳过(手动: opencode-feishu setup)\n');
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
async function setupQiweiInteractive() {
|
|
601
|
-
console.log('\n 💬 企微配置 — 需要企业微信管理后台的 botId 和 secret\n');
|
|
512
|
+
// ponytail: merged setupFeishuInteractive + setupQiweiInteractive
|
|
513
|
+
async function setupChannelInteractive(emoji, name, cli, timeout, hint) {
|
|
514
|
+
console.log(`\n ${emoji} ${name}配置 — ${hint}\n`);
|
|
602
515
|
try {
|
|
603
516
|
const { execSync } = await import('node:child_process');
|
|
604
|
-
execSync(
|
|
605
|
-
console.log(
|
|
517
|
+
execSync(cli, { stdio: 'inherit', timeout });
|
|
518
|
+
console.log(` ✅ ${name}配置完成\n`);
|
|
606
519
|
}
|
|
607
520
|
catch {
|
|
608
|
-
console.log(
|
|
521
|
+
console.log(` ⚠️ ${name}配置跳过(手动: ${cli})\n`);
|
|
609
522
|
}
|
|
610
523
|
}
|
|
524
|
+
async function setupFeishuInteractive() { return setupChannelInteractive('📱', '飞书', 'opencode-feishu setup', 300000, '终端将显示二维码,用飞书 App 扫码'); }
|
|
525
|
+
async function setupQiweiInteractive() { return setupChannelInteractive('💬', '企微', 'opencode-qiwei setup', 120000, '需要企业微信管理后台的 botId 和 secret'); }
|
|
611
526
|
async function startFeishu(feishuConfig) {
|
|
612
527
|
if (!existsSync(feishuConfig))
|
|
613
528
|
return;
|
|
@@ -648,7 +563,10 @@ async function cmdStart() {
|
|
|
648
563
|
const hasSoul = existsSync(join(cwd, 'soul', 'SOUL.md'));
|
|
649
564
|
if (!hasSoul) {
|
|
650
565
|
console.log('\n 📝 首次运行,自动初始化...');
|
|
651
|
-
|
|
566
|
+
// ponytail: auto-init handled by cmdSetup now, just ensure dirs exist
|
|
567
|
+
const autoDirs = ['soul', 'skills', 'knowledge', 'memory/short-term', 'memory/long-term', 'heartbeat', '.opencode'];
|
|
568
|
+
for (const d of autoDirs)
|
|
569
|
+
mkdirSync(join(cwd, d), { recursive: true });
|
|
652
570
|
const autoDir = join(cwd, '__auto__');
|
|
653
571
|
if (existsSync(autoDir)) {
|
|
654
572
|
const { readdirSync: rd, renameSync, rmdirSync } = await import('node:fs');
|
|
@@ -661,10 +579,8 @@ async function cmdStart() {
|
|
|
661
579
|
rmdirSync(autoDir);
|
|
662
580
|
}
|
|
663
581
|
}
|
|
664
|
-
let serveRunning = false;
|
|
665
582
|
try {
|
|
666
583
|
await fetch('http://localhost:19876/health', { signal: AbortSignal.timeout(2000) });
|
|
667
|
-
serveRunning = true;
|
|
668
584
|
console.log(' 🧠 opencode serve: 运行中');
|
|
669
585
|
}
|
|
670
586
|
catch {
|
|
@@ -729,32 +645,32 @@ async function cmdStart() {
|
|
|
729
645
|
if (answer)
|
|
730
646
|
await setupQiweiInteractive();
|
|
731
647
|
}
|
|
732
|
-
if (
|
|
648
|
+
if (hasFeishu)
|
|
733
649
|
await startFeishu(feishuConfig);
|
|
734
|
-
if (
|
|
650
|
+
if (hasQiwei)
|
|
735
651
|
await startQiwei(qiweiConfig);
|
|
736
652
|
try {
|
|
737
653
|
const { execSync } = await import('node:child_process');
|
|
738
|
-
if (existsSync(join(cwd, 'node_modules', '.bin', '
|
|
739
|
-
execSync(join(cwd, 'node_modules', '.bin', '
|
|
654
|
+
if (existsSync(join(cwd, 'node_modules', '.bin', 'agent-soul-heartbeat'))) {
|
|
655
|
+
execSync(join(cwd, 'node_modules', '.bin', 'agent-soul-heartbeat'), { cwd, stdio: 'pipe', timeout: 60000, encoding: 'utf-8' });
|
|
740
656
|
}
|
|
741
657
|
else {
|
|
742
|
-
execSync('
|
|
658
|
+
execSync('agent-soul-heartbeat', { cwd, stdio: 'pipe', timeout: 60000, encoding: 'utf-8' });
|
|
743
659
|
}
|
|
744
660
|
}
|
|
745
661
|
catch { }
|
|
746
662
|
console.log('\n ✅ 魂器已就绪!');
|
|
747
|
-
if (
|
|
663
|
+
if (hasFeishu)
|
|
748
664
|
console.log(' 飞书中 @机器人 即可对话');
|
|
749
|
-
console.log('
|
|
665
|
+
console.log(' agent-soul-framework doctor # 查看完整状态\n');
|
|
750
666
|
}
|
|
751
667
|
// ─── 主入口 ────────────────────────────────────────────────
|
|
752
668
|
const pkgVersion = loadJSON(join(PACKAGE_ROOT, 'package.json')).version || '3.0.0';
|
|
753
669
|
const HELP = '\n 魂器 · Agent Soul Framework v' + pkgVersion + '\n\n' +
|
|
754
|
-
' 用法:
|
|
670
|
+
' 用法: agent-soul-framework <command> [options]\n\n' +
|
|
755
671
|
' 核心命令:\n' +
|
|
756
672
|
' start 一键启动(引擎+飞书+企微+心跳)\n' +
|
|
757
|
-
'
|
|
673
|
+
' setup 初始化/配置项目(模板+心跳+crontab)\n' +
|
|
758
674
|
' status 查看系统状态(记忆/知识/引擎)\n' +
|
|
759
675
|
' heartbeat 执行一次心跳(同步 + 索引 + 任务)\n' +
|
|
760
676
|
' search <关键词> 统一记忆搜索(会话 + 文件 + MEMORY.md)\n' +
|
|
@@ -769,13 +685,12 @@ const HELP = '\n 魂器 · Agent Soul Framework v' + pkgVersion + '\n\n' +
|
|
|
769
685
|
' interactive 启动交互式 TUI(需在项目目录)\n' +
|
|
770
686
|
' config 查看配置路径\n' +
|
|
771
687
|
' doctor 系统诊断(检查所有组件状态)\n' +
|
|
772
|
-
' setup 自动配置(模板+心跳+crontab)\n\n' +
|
|
773
688
|
' 示例:\n' +
|
|
774
|
-
'
|
|
775
|
-
'
|
|
776
|
-
'
|
|
777
|
-
'
|
|
778
|
-
'
|
|
689
|
+
' agent-soul-framework status\n' +
|
|
690
|
+
' agent-soul-framework search "拍照"\n' +
|
|
691
|
+
' agent-soul-framework memory status\n' +
|
|
692
|
+
' agent-soul-framework knowledge daily\n' +
|
|
693
|
+
' agent-soul-framework heartbeat\n';
|
|
779
694
|
async function main() {
|
|
780
695
|
const args = process.argv.slice(2);
|
|
781
696
|
const cmd = args[0];
|
|
@@ -789,8 +704,8 @@ async function main() {
|
|
|
789
704
|
case 'start':
|
|
790
705
|
await cmdStart();
|
|
791
706
|
break;
|
|
792
|
-
case '
|
|
793
|
-
await
|
|
707
|
+
case 'setup':
|
|
708
|
+
await cmdSetup();
|
|
794
709
|
break;
|
|
795
710
|
case 'status':
|
|
796
711
|
await cmdStatus();
|
|
@@ -819,11 +734,8 @@ async function main() {
|
|
|
819
734
|
case 'doctor':
|
|
820
735
|
await cmdDoctor();
|
|
821
736
|
break;
|
|
822
|
-
case 'setup':
|
|
823
|
-
await cmdSetup();
|
|
824
|
-
break;
|
|
825
737
|
default:
|
|
826
|
-
console.log('未知命令: ' + cmd + '\n用法:
|
|
738
|
+
console.log('未知命令: ' + cmd + '\n用法: agent-soul-framework help');
|
|
827
739
|
}
|
|
828
740
|
}
|
|
829
741
|
main().catch(console.error);
|