evolclaw 2.2.0 → 2.4.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 +49 -27
- package/data/evolclaw.sample.json +6 -3
- package/dist/agents/claude-runner.js +125 -52
- package/dist/agents/codex-runner.js +10 -5
- package/dist/agents/gemini-runner.js +425 -0
- package/dist/channels/aun.js +283 -95
- package/dist/channels/feishu.js +556 -96
- package/dist/channels/wechat.js +98 -74
- package/dist/cli.js +232 -57
- package/dist/config.js +185 -31
- package/dist/core/channel-loader.js +11 -4
- package/dist/core/command-handler.js +803 -247
- package/dist/core/interaction-router.js +68 -0
- package/dist/core/message/message-bridge.js +217 -0
- package/dist/core/{message-processor.js → message/message-processor.js} +411 -124
- package/dist/core/{message-queue.js → message/message-queue.js} +1 -1
- package/dist/{utils → core/message}/stream-debouncer.js +1 -1
- package/dist/{utils → core/message}/stream-flusher.js +73 -13
- package/dist/core/permission.js +212 -11
- package/dist/core/{adapters → session/adapters}/claude-session-file-adapter.js +2 -2
- package/dist/core/{adapters → session/adapters}/codex-session-file-adapter.js +117 -52
- package/dist/core/session/adapters/gemini-session-file-adapter.js +177 -0
- package/dist/{utils → core/session}/session-file-health.js +1 -1
- package/dist/core/{session-manager.js → session/session-manager.js} +61 -11
- package/dist/index.js +140 -57
- package/dist/{core/ipc-server.js → ipc.js} +36 -1
- package/dist/types.js +3 -0
- package/dist/utils/cross-platform.js +38 -1
- package/dist/utils/error-utils.js +130 -5
- package/dist/utils/init-channel.js +649 -0
- package/dist/utils/init.js +55 -150
- package/dist/utils/logger.js +8 -3
- package/dist/utils/media-cache.js +207 -0
- package/dist/{core → utils}/stats-collector.js +16 -0
- package/package.json +3 -3
- package/dist/core/message-bridge.js +0 -187
- package/dist/utils/init-feishu.js +0 -263
- package/dist/utils/init-wechat.js +0 -172
- package/dist/utils/ipc-client.js +0 -36
- package/dist/utils/permission-utils.js +0 -71
- /package/dist/{utils → core/message}/message-cache.js +0 -0
- /package/dist/{utils → core/message}/stream-idle-monitor.js +0 -0
- /package/dist/core/{session-file-adapter.js → session/session-file-adapter.js} +0 -0
package/dist/cli.js
CHANGED
|
@@ -7,10 +7,9 @@ import { resolveRoot, resolvePaths, ensureDataDirs, getPackageRoot } from './pat
|
|
|
7
7
|
import { loadConfig, validateConfigIntegrity, resolveAnthropicConfig } from './config.js';
|
|
8
8
|
import { migrateProject } from './utils/migrate-project.js';
|
|
9
9
|
import readline from 'readline';
|
|
10
|
-
import { cmdInit
|
|
11
|
-
import { ipcQuery } from './
|
|
12
|
-
import { cmdInitWechat } from './utils/init-
|
|
13
|
-
import { cmdInitFeishu } from './utils/init-feishu.js';
|
|
10
|
+
import { cmdInit } from './utils/init.js';
|
|
11
|
+
import { ipcQuery } from './ipc.js';
|
|
12
|
+
import { cmdInitWechat, cmdInitFeishu, cmdInitAun } from './utils/init-channel.js';
|
|
14
13
|
import * as platform from './utils/cross-platform.js';
|
|
15
14
|
import { EventBus } from './core/event-bus.js';
|
|
16
15
|
// Suppress Node.js ExperimentalWarning (e.g. SQLite) from cluttering CLI output
|
|
@@ -200,9 +199,11 @@ async function cmdStart() {
|
|
|
200
199
|
process.exit(1);
|
|
201
200
|
}
|
|
202
201
|
// 检查是否有残留进程(PID 文件已丢失但进程还在)
|
|
202
|
+
// 只清理属于当前 EVOLCLAW_HOME 的进程,避免误杀其他实例
|
|
203
203
|
let hasOrphan = false;
|
|
204
204
|
const evolclawMain = path.join(getPackageRoot(), 'dist', 'index.js');
|
|
205
|
-
const
|
|
205
|
+
const allPids = platform.findProcesses(evolclawMain.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
206
|
+
const orphanPids = allPids.filter(pid => platform.getProcessEnv(pid, 'EVOLCLAW_HOME') === p.root);
|
|
206
207
|
if (orphanPids.length > 0) {
|
|
207
208
|
console.log(`⚠ 发现 ${orphanPids.length} 个残留进程,正在清理...`);
|
|
208
209
|
for (const p of orphanPids) {
|
|
@@ -231,6 +232,7 @@ async function cmdStart() {
|
|
|
231
232
|
stdio: ['ignore', out, err],
|
|
232
233
|
env: {
|
|
233
234
|
...process.env,
|
|
235
|
+
EVOLCLAW_HOME: p.root,
|
|
234
236
|
LOG_LEVEL: process.env.LOG_LEVEL || 'INFO',
|
|
235
237
|
MESSAGE_LOG: process.env.MESSAGE_LOG || 'true',
|
|
236
238
|
EVENT_LOG: process.env.EVENT_LOG || 'true',
|
|
@@ -385,27 +387,37 @@ function formatTimeAgo(ms) {
|
|
|
385
387
|
return `${day}天前`;
|
|
386
388
|
}
|
|
387
389
|
function showConfigChannels(config) {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
390
|
+
const groups = [];
|
|
391
|
+
const channelChecks = [
|
|
392
|
+
{ type: 'feishu', isValid: (inst) => !!inst.appId && inst.enabled !== false },
|
|
393
|
+
{ type: 'wechat', isValid: (inst) => !!inst.token && inst.enabled !== false },
|
|
394
|
+
{ type: 'aun', isValid: (inst) => !!inst.aid && inst.enabled !== false && !inst.aid.includes('your-') && !inst.aid.includes('placeholder') },
|
|
395
|
+
];
|
|
396
|
+
for (const { type, isValid } of channelChecks) {
|
|
397
|
+
const raw = config.channels?.[type];
|
|
398
|
+
if (!raw)
|
|
399
|
+
continue;
|
|
400
|
+
if (Array.isArray(raw)) {
|
|
401
|
+
const names = raw.filter(isValid).map((inst) => inst.name || type);
|
|
402
|
+
if (names.length > 0)
|
|
403
|
+
groups.push({ type, instances: names });
|
|
404
|
+
}
|
|
405
|
+
else if (isValid(raw)) {
|
|
406
|
+
groups.push({ type, instances: [raw.name || type] });
|
|
407
|
+
}
|
|
401
408
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
409
|
+
if (groups.length > 0) {
|
|
410
|
+
for (const g of groups) {
|
|
411
|
+
if (g.instances.length === 1) {
|
|
412
|
+
console.log(` ${g.instances[0]}: ✓ Configured`);
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
console.log(` ${g.type}: [${g.instances.join(', ')}]`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
406
418
|
}
|
|
407
419
|
else {
|
|
408
|
-
console.log('
|
|
420
|
+
console.log(' (no channels configured)');
|
|
409
421
|
}
|
|
410
422
|
}
|
|
411
423
|
async function cmdStatus() {
|
|
@@ -421,8 +433,11 @@ async function cmdStatus() {
|
|
|
421
433
|
console.log(` Uptime: ${info.uptime}`);
|
|
422
434
|
if (info.cpu)
|
|
423
435
|
console.log(` CPU: ${info.cpu}%`);
|
|
424
|
-
if (info.memory)
|
|
425
|
-
|
|
436
|
+
if (info.memory) {
|
|
437
|
+
const memKB = parseInt(info.memory, 10);
|
|
438
|
+
const memStr = memKB >= 1024 ? `${(memKB / 1024).toFixed(0)} MB` : `${memKB} KB`;
|
|
439
|
+
console.log(` Memory: ${memStr}`);
|
|
440
|
+
}
|
|
426
441
|
}
|
|
427
442
|
catch { }
|
|
428
443
|
console.log(` EVOLCLAW_HOME: ${resolveRoot()}`);
|
|
@@ -493,13 +508,29 @@ async function cmdStatus() {
|
|
|
493
508
|
const status = await ipcQuery(p.socket, { type: 'status' });
|
|
494
509
|
if (status) {
|
|
495
510
|
console.log('🔌 Channels (live):');
|
|
511
|
+
// Group channels by channelType
|
|
512
|
+
const groups = new Map();
|
|
496
513
|
for (const [name, ch] of Object.entries(status.channels)) {
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
514
|
+
const type = ch.channelType || name;
|
|
515
|
+
if (!groups.has(type))
|
|
516
|
+
groups.set(type, []);
|
|
517
|
+
groups.get(type).push({ name, ch: ch });
|
|
518
|
+
}
|
|
519
|
+
for (const [type, instances] of groups) {
|
|
520
|
+
if (instances.length === 1) {
|
|
521
|
+
// Single instance: show instance name directly
|
|
522
|
+
const { name, ch } = instances[0];
|
|
523
|
+
const label = ch.connected ? '✓ Connected' : ch.reconnectAttempt ? `⏳ Reconnecting (${ch.reconnectAttempt}/${ch.maxAttempts})` : '✗ Disconnected';
|
|
524
|
+
console.log(` ${name}: ${label}`);
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
// Multi-instance: feishu [name1 ✓, name2 ✗]
|
|
528
|
+
const parts = instances.map(({ name, ch }) => {
|
|
529
|
+
const icon = ch.connected ? '✓' : ch.reconnectAttempt ? '⏳' : '✗';
|
|
530
|
+
return `${name} ${icon}`;
|
|
531
|
+
});
|
|
532
|
+
console.log(` ${type}: [${parts.join(', ')}]`);
|
|
533
|
+
}
|
|
503
534
|
}
|
|
504
535
|
if (status.stats) {
|
|
505
536
|
console.log('');
|
|
@@ -530,30 +561,119 @@ async function cmdStatus() {
|
|
|
530
561
|
const sizeMB = (stat.size / 1024 / 1024).toFixed(1);
|
|
531
562
|
console.log(` Main log: ${mainLog} (${sizeMB} MB)`);
|
|
532
563
|
console.log('');
|
|
533
|
-
console.log('📝 Recent activity (last
|
|
564
|
+
console.log('📝 Recent activity (last 30 lines):');
|
|
534
565
|
const content = fs.readFileSync(mainLog, 'utf-8').trim().split('\n');
|
|
535
|
-
console.log(content.slice(-
|
|
566
|
+
console.log(content.slice(-30).map(l => ` ${l}`).join('\n'));
|
|
536
567
|
}
|
|
537
568
|
else {
|
|
538
569
|
console.log(' (no log file yet)');
|
|
539
570
|
}
|
|
540
571
|
}
|
|
541
|
-
|
|
572
|
+
// Log line pattern: [timestamp] [LEVEL] [Module?] message
|
|
573
|
+
const LOG_RE = /^(\[[^\]]+\]) (\[(?:INFO|WARN|ERROR|DEBUG)\]) ((?:\[[^\]]+\] )*)(.*)$/;
|
|
574
|
+
const MAX_MSG = 200; // truncate long messages
|
|
575
|
+
function makeColors(enabled) {
|
|
576
|
+
const e = (code) => enabled ? code : '';
|
|
577
|
+
return {
|
|
578
|
+
reset: e('\x1b[0m'), dim: e('\x1b[2m'), bold: e('\x1b[1m'),
|
|
579
|
+
red: e('\x1b[31m'), yellow: e('\x1b[33m'), cyan: e('\x1b[36m'),
|
|
580
|
+
magenta: e('\x1b[35m'), gray: e('\x1b[90m'),
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
function renderLogLine(line, opts) {
|
|
584
|
+
const m = line.match(LOG_RE);
|
|
585
|
+
if (!m)
|
|
586
|
+
return line; // passthrough non-standard lines (stack traces etc.)
|
|
587
|
+
const [, ts, levelTag, modulePart, msg] = m;
|
|
588
|
+
const level = levelTag.slice(1, -1); // strip brackets
|
|
589
|
+
// Level filter
|
|
590
|
+
if (opts.level) {
|
|
591
|
+
const want = opts.level.toUpperCase();
|
|
592
|
+
if (want === 'ERROR' && level !== 'ERROR')
|
|
593
|
+
return null;
|
|
594
|
+
if (want === 'WARN' && level !== 'WARN' && level !== 'ERROR')
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
// Module filter (case-insensitive substring match)
|
|
598
|
+
if (opts.module) {
|
|
599
|
+
const mod = modulePart.toLowerCase();
|
|
600
|
+
if (!mod.includes(opts.module.toLowerCase()))
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
// Truncate long messages (always, regardless of color)
|
|
604
|
+
const truncated = msg.length > MAX_MSG ? msg.slice(0, MAX_MSG) + '…' : msg;
|
|
605
|
+
const C = makeColors(opts.color);
|
|
606
|
+
// Color by level
|
|
607
|
+
const levelColor = level === 'ERROR' ? C.red : level === 'WARN' ? C.yellow : level === 'DEBUG' ? C.gray : '';
|
|
608
|
+
// Highlight user messages: [channel] channelId: text
|
|
609
|
+
const isUserMsg = modulePart && /^\S+: .+$/.test(truncated);
|
|
610
|
+
const renderedMsg = isUserMsg
|
|
611
|
+
? C.cyan + truncated + C.reset
|
|
612
|
+
: levelColor + truncated + C.reset;
|
|
613
|
+
return (C.dim + ts + C.reset + ' ' +
|
|
614
|
+
levelColor + C.bold + levelTag + C.reset + ' ' +
|
|
615
|
+
C.magenta + modulePart.trimEnd() + C.reset +
|
|
616
|
+
(modulePart ? ' ' : '') +
|
|
617
|
+
renderedMsg);
|
|
618
|
+
}
|
|
619
|
+
function cmdLogs(args) {
|
|
620
|
+
const raw = args.includes('--raw');
|
|
621
|
+
const noColor = args.includes('--no-color');
|
|
622
|
+
const levelIdx = args.indexOf('--level');
|
|
623
|
+
const moduleIdx = args.indexOf('--module');
|
|
624
|
+
const level = levelIdx !== -1 ? args[levelIdx + 1] : undefined;
|
|
625
|
+
const module = moduleIdx !== -1 ? args[moduleIdx + 1] : undefined;
|
|
542
626
|
const p = resolvePaths();
|
|
543
627
|
const mainLog = path.join(p.logs, 'evolclaw.log');
|
|
544
628
|
if (!fs.existsSync(mainLog)) {
|
|
545
629
|
console.log(`❌ Log file not found: ${mainLog}`);
|
|
546
630
|
process.exit(1);
|
|
547
631
|
}
|
|
632
|
+
if (raw) {
|
|
633
|
+
// Raw mode: plain tail -f, no rendering at all
|
|
634
|
+
if (platform.isWindows) {
|
|
635
|
+
const tail = platform.tailFile(mainLog);
|
|
636
|
+
platform.onShutdown(() => tail.abort());
|
|
637
|
+
}
|
|
638
|
+
else {
|
|
639
|
+
const child = spawn('tail', ['-f', '-n', '50', mainLog], { stdio: 'inherit' });
|
|
640
|
+
child.on('exit', (code) => process.exit(code || 0));
|
|
641
|
+
}
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
// Rendered mode: always filter+truncate, color depends on TTY
|
|
645
|
+
const useColor = !noColor && !!process.stdout.isTTY;
|
|
646
|
+
const opts = { level, module, color: useColor };
|
|
647
|
+
function processLine(line) {
|
|
648
|
+
const rendered = renderLogLine(line, opts);
|
|
649
|
+
if (rendered !== null)
|
|
650
|
+
process.stdout.write(rendered + '\n');
|
|
651
|
+
}
|
|
548
652
|
if (platform.isWindows) {
|
|
549
|
-
// Windows:
|
|
550
|
-
const
|
|
551
|
-
|
|
653
|
+
// Windows: read existing content + watch
|
|
654
|
+
const existing = fs.readFileSync(mainLog, 'utf-8').split('\n').slice(-50);
|
|
655
|
+
existing.forEach(processLine);
|
|
656
|
+
let size = fs.statSync(mainLog).size;
|
|
657
|
+
const watcher = fs.watch(mainLog, () => {
|
|
658
|
+
const newSize = fs.statSync(mainLog).size;
|
|
659
|
+
if (newSize <= size)
|
|
660
|
+
return;
|
|
661
|
+
const buf = Buffer.alloc(newSize - size);
|
|
662
|
+
const fd = fs.openSync(mainLog, 'r');
|
|
663
|
+
fs.readSync(fd, buf, 0, buf.length, size);
|
|
664
|
+
fs.closeSync(fd);
|
|
665
|
+
size = newSize;
|
|
666
|
+
buf.toString().split('\n').forEach(l => l && processLine(l));
|
|
667
|
+
});
|
|
668
|
+
platform.onShutdown(() => watcher.close());
|
|
552
669
|
}
|
|
553
670
|
else {
|
|
554
|
-
// Unix:
|
|
555
|
-
const child = spawn('tail', ['-f',
|
|
671
|
+
// Unix: spawn tail -f, pipe through renderer
|
|
672
|
+
const child = spawn('tail', ['-f', '-n', '50', mainLog]);
|
|
673
|
+
const rl = readline.createInterface({ input: child.stdout });
|
|
674
|
+
rl.on('line', processLine);
|
|
556
675
|
child.on('exit', (code) => process.exit(code || 0));
|
|
676
|
+
platform.onShutdown(() => { child.kill(); });
|
|
557
677
|
}
|
|
558
678
|
}
|
|
559
679
|
/**
|
|
@@ -617,6 +737,13 @@ async function cmdRestartMonitor() {
|
|
|
617
737
|
// 通知由新进程自行发送(channel-agnostic),此处不再调用 notifyChannel
|
|
618
738
|
process.exit(0);
|
|
619
739
|
}
|
|
740
|
+
// 启动失败 — 测试环境下跳过 self-heal(避免 claude -p 污染会话列表、误杀生产进程)
|
|
741
|
+
if (p.root.startsWith('/tmp/') || process.env.EVOLCLAW_TEST === '1') {
|
|
742
|
+
log('❌ Service failed to start (test environment detected, skipping self-heal)');
|
|
743
|
+
await notifyChannel(p, pendingInfo, '❌ 服务启动失败(测试环境,已跳过自动修复)', log);
|
|
744
|
+
cleanupPendingFile(pendingFile, log);
|
|
745
|
+
process.exit(1);
|
|
746
|
+
}
|
|
620
747
|
// 启动失败,进入 self-heal 循环
|
|
621
748
|
log('❌ Service failed to start, entering self-heal loop');
|
|
622
749
|
eventBus.publish({ type: 'self-heal:started', reason: 'Service failed to start after restart' });
|
|
@@ -719,7 +846,13 @@ async function spawnAndWaitReady(p, log, timeout) {
|
|
|
719
846
|
fs.unlinkSync(p.readySignal);
|
|
720
847
|
}
|
|
721
848
|
catch { }
|
|
722
|
-
//
|
|
849
|
+
// 杀掉可能残留的进程(先读 PID 再删文件,避免数据库锁)
|
|
850
|
+
try {
|
|
851
|
+
const stalePid = parseInt(fs.readFileSync(p.pid, 'utf-8').trim(), 10);
|
|
852
|
+
if (!isNaN(stalePid))
|
|
853
|
+
platform.killProcess(stalePid, true);
|
|
854
|
+
}
|
|
855
|
+
catch { }
|
|
723
856
|
try {
|
|
724
857
|
fs.unlinkSync(p.pid);
|
|
725
858
|
}
|
|
@@ -734,6 +867,7 @@ async function spawnAndWaitReady(p, log, timeout) {
|
|
|
734
867
|
stdio: ['ignore', out, err],
|
|
735
868
|
env: {
|
|
736
869
|
...process.env,
|
|
870
|
+
EVOLCLAW_HOME: p.root,
|
|
737
871
|
LOG_LEVEL: process.env.LOG_LEVEL || 'INFO',
|
|
738
872
|
MESSAGE_LOG: process.env.MESSAGE_LOG || 'true',
|
|
739
873
|
EVENT_LOG: process.env.EVENT_LOG || 'true',
|
|
@@ -813,6 +947,7 @@ async function invokeClaude(p, attempt, maxAttempts, timeout, log) {
|
|
|
813
947
|
'-p', prompt,
|
|
814
948
|
'--allowedTools', 'Read,Write,Edit,Bash,Glob,Grep',
|
|
815
949
|
'--output-format', 'text',
|
|
950
|
+
'--no-session-persistence',
|
|
816
951
|
], {
|
|
817
952
|
cwd: projectDir,
|
|
818
953
|
timeout,
|
|
@@ -854,6 +989,28 @@ function archiveSelfHealLog(p, log) {
|
|
|
854
989
|
fs.renameSync(p.selfHealLog, archivePath);
|
|
855
990
|
log(`Archived self-heal log to ${archivePath}`);
|
|
856
991
|
}
|
|
992
|
+
/**
|
|
993
|
+
* Resolve a channel instance name to its type and config object.
|
|
994
|
+
* Searches across all channel types (feishu, wechat, aun) for a matching instance.
|
|
995
|
+
*/
|
|
996
|
+
function resolveInstanceConfig(config, instanceName) {
|
|
997
|
+
for (const type of ['feishu', 'wechat', 'aun']) {
|
|
998
|
+
const raw = config.channels?.[type];
|
|
999
|
+
if (!raw)
|
|
1000
|
+
continue;
|
|
1001
|
+
if (Array.isArray(raw)) {
|
|
1002
|
+
const inst = raw.find((i) => i.name === instanceName);
|
|
1003
|
+
if (inst)
|
|
1004
|
+
return { type, config: inst };
|
|
1005
|
+
}
|
|
1006
|
+
else {
|
|
1007
|
+
const name = raw.name || type;
|
|
1008
|
+
if (name === instanceName)
|
|
1009
|
+
return { type, config: raw };
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
return null;
|
|
1013
|
+
}
|
|
857
1014
|
/**
|
|
858
1015
|
* 通过对应渠道 API 发送通知(轻量级,不依赖 Channel 实例)
|
|
859
1016
|
* 支持 feishu / wechat,根据 pendingInfo.channel 路由
|
|
@@ -865,14 +1022,20 @@ async function notifyChannel(p, pendingInfo, message, log) {
|
|
|
865
1022
|
if (!fs.existsSync(configPath))
|
|
866
1023
|
return;
|
|
867
1024
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
868
|
-
|
|
1025
|
+
const resolved = resolveInstanceConfig(config, pendingInfo.channel);
|
|
1026
|
+
if (!resolved) {
|
|
1027
|
+
log(`Channel instance "${pendingInfo.channel}" not found in config`);
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
if (resolved.type === 'feishu') {
|
|
869
1031
|
try {
|
|
870
|
-
|
|
1032
|
+
const inst = resolved.config;
|
|
1033
|
+
if (!inst.appId || !inst.appSecret)
|
|
871
1034
|
return;
|
|
872
1035
|
const lark = await import('@larksuiteoapi/node-sdk');
|
|
873
1036
|
const client = new lark.Client({
|
|
874
|
-
appId:
|
|
875
|
-
appSecret:
|
|
1037
|
+
appId: inst.appId,
|
|
1038
|
+
appSecret: inst.appSecret,
|
|
876
1039
|
});
|
|
877
1040
|
if (pendingInfo.rootId) {
|
|
878
1041
|
await client.im.message.reply({
|
|
@@ -900,13 +1063,14 @@ async function notifyChannel(p, pendingInfo, message, log) {
|
|
|
900
1063
|
log(`Feishu notification failed: ${error.message?.slice(0, 200) || error}`);
|
|
901
1064
|
}
|
|
902
1065
|
}
|
|
903
|
-
else if (
|
|
1066
|
+
else if (resolved.type === 'wechat') {
|
|
904
1067
|
try {
|
|
905
|
-
|
|
1068
|
+
const inst = resolved.config;
|
|
1069
|
+
if (!inst.token)
|
|
906
1070
|
return;
|
|
907
1071
|
const crypto = await import('node:crypto');
|
|
908
|
-
const baseUrl = (
|
|
909
|
-
const token =
|
|
1072
|
+
const baseUrl = (inst.baseUrl || 'https://ilinkai.weixin.qq.com').replace(/\/$/, '');
|
|
1073
|
+
const token = inst.token;
|
|
910
1074
|
// 读取缓存的 context_token
|
|
911
1075
|
const syncBufPath = path.join(p.dataDir, 'wechat-context-tokens.json');
|
|
912
1076
|
let contextToken;
|
|
@@ -1030,7 +1194,7 @@ async function cmdDiagnose() {
|
|
|
1030
1194
|
}
|
|
1031
1195
|
// 4. 检查数据库
|
|
1032
1196
|
try {
|
|
1033
|
-
const { SessionManager } = await import('./core/session-manager.js');
|
|
1197
|
+
const { SessionManager } = await import('./core/session/session-manager.js');
|
|
1034
1198
|
const eventBus = new EventBus();
|
|
1035
1199
|
new SessionManager(p.db, eventBus);
|
|
1036
1200
|
console.log(`[diagnose] ✓ 数据库初始化成功: ${p.db}`);
|
|
@@ -1071,20 +1235,28 @@ async function cmdDiagnose() {
|
|
|
1071
1235
|
}
|
|
1072
1236
|
async function cmdTui() {
|
|
1073
1237
|
const config = loadConfig();
|
|
1074
|
-
|
|
1238
|
+
// Find the first AUN instance (TUI connects to one AUN instance)
|
|
1239
|
+
const aunResolved = resolveInstanceConfig(config, 'aun');
|
|
1240
|
+
const aun = aunResolved?.type === 'aun' ? aunResolved.config : null;
|
|
1075
1241
|
if (!aun?.owner || !aun?.aid) {
|
|
1076
1242
|
console.error('[tui] AUN 未配置,请先运行: evolclaw init aun');
|
|
1077
1243
|
process.exit(1);
|
|
1078
1244
|
}
|
|
1079
|
-
//
|
|
1080
|
-
const
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1245
|
+
// TUI requires Python + aun_core (independent of init aun which is now pure TS)
|
|
1246
|
+
const pythonCheck = aun.pythonBin || process.env.AUN_PYTHON || 'python3';
|
|
1247
|
+
if (!platform.commandExists(pythonCheck)) {
|
|
1248
|
+
console.error(`[tui] Python 未找到 (${pythonCheck})`);
|
|
1249
|
+
console.error(' → TUI 依赖 Python 和 aun-core: pip3 install aun-core');
|
|
1084
1250
|
process.exit(1);
|
|
1085
1251
|
}
|
|
1086
1252
|
const pythonBin = aun.pythonBin || process.env.AUN_PYTHON || 'python3';
|
|
1087
1253
|
const cliScript = path.join(getPackageRoot(), 'aun', 'aun_cli.py');
|
|
1254
|
+
if (!fs.existsSync(cliScript)) {
|
|
1255
|
+
console.error(`[tui] aun_cli.py 不存在: ${cliScript}`);
|
|
1256
|
+
console.error(' → TUI 需要 AUN CLI 工具,请确认源码目录包含 aun/aun_cli.py');
|
|
1257
|
+
console.error(' → 安装: pip3 install aun-core && 从源码仓库获取 aun_cli.py');
|
|
1258
|
+
process.exit(1);
|
|
1259
|
+
}
|
|
1088
1260
|
const child = spawn(pythonBin, [cliScript, '-a', aun.owner, '-t', aun.aid], { stdio: 'inherit' });
|
|
1089
1261
|
child.on('exit', (code) => process.exit(code ?? 0));
|
|
1090
1262
|
}
|
|
@@ -1119,7 +1291,7 @@ export async function main(args) {
|
|
|
1119
1291
|
await cmdStatus();
|
|
1120
1292
|
break;
|
|
1121
1293
|
case 'logs':
|
|
1122
|
-
cmdLogs();
|
|
1294
|
+
cmdLogs(args.slice(1));
|
|
1123
1295
|
break;
|
|
1124
1296
|
case 'restart-monitor':
|
|
1125
1297
|
await cmdRestartMonitor();
|
|
@@ -1145,7 +1317,10 @@ Commands:
|
|
|
1145
1317
|
stop 停止服务
|
|
1146
1318
|
restart 重启服务
|
|
1147
1319
|
status 查看状态
|
|
1148
|
-
logs 查看日志 (tail -f)
|
|
1320
|
+
logs 查看日志 (tail -f, 着色渲染)
|
|
1321
|
+
--level error|warn 只显示指定级别及以上
|
|
1322
|
+
--module <name> 只显示指定模块(如 feishu、AgentRunner)
|
|
1323
|
+
--raw 原始输出,不着色
|
|
1149
1324
|
tui 启动 AUN TUI 客户端
|
|
1150
1325
|
diagnose 诊断启动环境(配置、数据库、进程)
|
|
1151
1326
|
mv <old> <new> 迁移项目目录(保留 Claude/Codex/EvolClaw 会话)
|