evolclaw 2.4.0 → 2.5.1
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 +33 -14
- package/dist/agents/claude-runner.js +269 -23
- package/dist/agents/codex-runner.js +2 -8
- package/dist/agents/gemini-runner.js +1 -8
- package/dist/channels/aun.js +525 -53
- package/dist/channels/dingtalk.js +506 -0
- package/dist/channels/feishu.js +31 -231
- package/dist/channels/qqbot.js +391 -0
- package/dist/channels/wechat.js +36 -38
- package/dist/channels/wecom.js +549 -0
- package/dist/cli.js +86 -10
- package/dist/config.js +98 -2
- package/dist/core/command-handler.js +554 -130
- package/dist/core/message/message-bridge.js +26 -9
- package/dist/core/message/message-processor.js +152 -57
- package/dist/core/message/message-queue.js +48 -0
- package/dist/core/message/stream-flusher.js +2 -2
- package/dist/core/permission.js +7 -11
- package/dist/core/session/session-manager.js +21 -3
- package/dist/index.js +48 -13
- package/dist/ipc.js +14 -4
- package/dist/templates/skills.md +64 -0
- package/dist/utils/error-dict.js +63 -0
- package/dist/utils/error-utils.js +156 -56
- package/dist/utils/format.js +32 -0
- package/dist/utils/init-channel.js +752 -8
- package/dist/utils/init.js +85 -3
- package/dist/utils/media-cache.js +2 -0
- package/dist/utils/stats-collector.js +0 -8
- package/evolclaw-install.md +54 -0
- package/package.json +11 -4
package/dist/utils/init.js
CHANGED
|
@@ -376,7 +376,7 @@ async function offerRichContentRenderer(rl, config) {
|
|
|
376
376
|
// ==================== AUN AID Helpers ====================
|
|
377
377
|
// Moved to init-channel.ts
|
|
378
378
|
// ==================== Main ====================
|
|
379
|
-
export async function cmdInit() {
|
|
379
|
+
export async function cmdInit(options) {
|
|
380
380
|
const p = resolvePaths();
|
|
381
381
|
ensureDataDirs();
|
|
382
382
|
if (fs.existsSync(p.pid)) {
|
|
@@ -393,6 +393,56 @@ export async function cmdInit() {
|
|
|
393
393
|
console.log(`❌ 找不到示例配置: ${sampleSrc}`);
|
|
394
394
|
return;
|
|
395
395
|
}
|
|
396
|
+
// 非交互式模式
|
|
397
|
+
if (options?.nonInteractive) {
|
|
398
|
+
const config = JSON.parse(fs.readFileSync(sampleSrc, 'utf-8'));
|
|
399
|
+
const defaultPath = options.defaultPath || path.join(os.homedir(), 'evolclaw-project');
|
|
400
|
+
if (!fs.existsSync(defaultPath))
|
|
401
|
+
fs.mkdirSync(defaultPath, { recursive: true });
|
|
402
|
+
config.projects.defaultPath = defaultPath;
|
|
403
|
+
config.projects.list = { [path.basename(defaultPath)]: defaultPath };
|
|
404
|
+
if (options.channel === 'aun' && options.aunAid) {
|
|
405
|
+
// 自动安装 AUN SDK
|
|
406
|
+
const { resolveAunCoreSdkPkg, npmInstallGlobal } = await import('./init-channel.js');
|
|
407
|
+
if (!resolveAunCoreSdkPkg()) {
|
|
408
|
+
console.log('正在安装 @eleans/aun-core-sdk...');
|
|
409
|
+
await npmInstallGlobal('@eleans/aun-core-sdk@latest');
|
|
410
|
+
}
|
|
411
|
+
// 创建 AID(如果本地不存在)
|
|
412
|
+
const aunPath = path.join(os.homedir(), '.aun');
|
|
413
|
+
const aidDir = path.join(aunPath, 'AIDs', options.aunAid);
|
|
414
|
+
if (!fs.existsSync(path.join(aidDir, 'private'))) {
|
|
415
|
+
const { AUNClient } = await import('@eleans/aun-core-sdk');
|
|
416
|
+
const client = new AUNClient({ aun_path: aunPath });
|
|
417
|
+
const domain = options.aunAid.split('.').slice(1).join('.');
|
|
418
|
+
client._gatewayUrl = `wss://gateway.${domain}:443/aun`;
|
|
419
|
+
await client.auth.createAid({ aid: options.aunAid });
|
|
420
|
+
// 写入初始 agent.md(initialized: false)
|
|
421
|
+
const agentName = options.aunAid.split('.')[0];
|
|
422
|
+
const agentMd = `---\naid: "${options.aunAid}"\nname: "${agentName}"\ntype: "ai"\nversion: "1.0.0"\ndescription: ""\ntags:\n - evolclaw\ninitialized: false\n---\n`;
|
|
423
|
+
try {
|
|
424
|
+
await client.auth.uploadAgentMd(agentMd);
|
|
425
|
+
fs.writeFileSync(path.join(aidDir, 'agent.md'), agentMd, 'utf-8');
|
|
426
|
+
}
|
|
427
|
+
catch { }
|
|
428
|
+
try {
|
|
429
|
+
await client.close();
|
|
430
|
+
}
|
|
431
|
+
catch { }
|
|
432
|
+
}
|
|
433
|
+
config.channels.aun = {
|
|
434
|
+
enabled: true,
|
|
435
|
+
aid: options.aunAid,
|
|
436
|
+
...(options.aunOwner && { owner: options.aunOwner }),
|
|
437
|
+
};
|
|
438
|
+
config.channels.defaultChannel = 'aun';
|
|
439
|
+
}
|
|
440
|
+
fs.writeFileSync(p.config, JSON.stringify(config, null, 2) + '\n');
|
|
441
|
+
console.log(`✓ 已创建配置文件: ${p.config}`);
|
|
442
|
+
setupEnvVar(resolveRoot());
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
// 交互式模式
|
|
396
446
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
397
447
|
try {
|
|
398
448
|
if (fs.existsSync(p.config)) {
|
|
@@ -434,6 +484,8 @@ export async function cmdInit() {
|
|
|
434
484
|
console.log(' 1. 飞书 (Feishu)');
|
|
435
485
|
console.log(' 2. 微信 (WeChat)');
|
|
436
486
|
console.log(' 3. AUN (AgentUnin.Network)');
|
|
487
|
+
console.log(' 4. 钉钉 (DingTalk)');
|
|
488
|
+
console.log(' 5. QQ 机器人 (QQBot)');
|
|
437
489
|
const channelChoice = (await ask(rl, '请选择 [1]: ')).trim() || '1';
|
|
438
490
|
if (channelChoice === '1') {
|
|
439
491
|
console.log('\n飞书配置方式:');
|
|
@@ -488,11 +540,41 @@ export async function cmdInit() {
|
|
|
488
540
|
config.channels.aun = {
|
|
489
541
|
enabled: true,
|
|
490
542
|
aid: result.aid,
|
|
491
|
-
|
|
543
|
+
owner: result.owner,
|
|
492
544
|
};
|
|
493
545
|
channelConfigured = true;
|
|
494
546
|
config.channels.defaultChannel = 'aun';
|
|
495
547
|
}
|
|
548
|
+
else if (channelChoice === '4') {
|
|
549
|
+
const { runDingtalkQrFlowSimple } = await import('./init-channel.js');
|
|
550
|
+
const result = await runDingtalkQrFlowSimple();
|
|
551
|
+
if (!result) {
|
|
552
|
+
console.log('已取消');
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
config.channels.dingtalk = {
|
|
556
|
+
enabled: true,
|
|
557
|
+
clientId: result.clientId,
|
|
558
|
+
clientSecret: result.clientSecret,
|
|
559
|
+
};
|
|
560
|
+
channelConfigured = true;
|
|
561
|
+
config.channels.defaultChannel = 'dingtalk';
|
|
562
|
+
}
|
|
563
|
+
else if (channelChoice === '5') {
|
|
564
|
+
const { runQQBotBindFlowSimple } = await import('./init-channel.js');
|
|
565
|
+
const result = await runQQBotBindFlowSimple();
|
|
566
|
+
if (!result) {
|
|
567
|
+
console.log('已取消');
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
config.channels.qqbot = {
|
|
571
|
+
enabled: true,
|
|
572
|
+
appId: result.appId,
|
|
573
|
+
clientSecret: result.clientSecret,
|
|
574
|
+
};
|
|
575
|
+
channelConfigured = true;
|
|
576
|
+
config.channels.defaultChannel = 'qqbot';
|
|
577
|
+
}
|
|
496
578
|
else {
|
|
497
579
|
console.log(' 无效选择,请重新输入');
|
|
498
580
|
}
|
|
@@ -514,7 +596,7 @@ export async function cmdInit() {
|
|
|
514
596
|
* Returns the user's choice, or null if cancelled.
|
|
515
597
|
*/
|
|
516
598
|
export async function selectInstance(rl, channelType, instances) {
|
|
517
|
-
const typeLabel = channelType === 'feishu' ? '飞书' : channelType === 'wechat' ? '微信' : 'AUN';
|
|
599
|
+
const typeLabel = channelType === 'feishu' ? '飞书' : channelType === 'wechat' ? '微信' : channelType === 'dingtalk' ? '钉钉' : channelType === 'qqbot' ? 'QQ机器人' : 'AUN';
|
|
518
600
|
console.log(`\n发现已有 ${typeLabel} 机器人:`);
|
|
519
601
|
const letters = 'abcdefghijklmnopqrstuvwxyz';
|
|
520
602
|
for (let i = 0; i < instances.length; i++) {
|
|
@@ -24,6 +24,8 @@ const ALLOWED_CDN_HOSTS = new Set([
|
|
|
24
24
|
'novac2c.cdn.weixin.qq.com',
|
|
25
25
|
'open.feishu.cn',
|
|
26
26
|
'internal-api-lark-file.feishu.cn',
|
|
27
|
+
'oapi.dingtalk.com',
|
|
28
|
+
'api.dingtalk.com',
|
|
27
29
|
]);
|
|
28
30
|
/** 私有 IP 段正则(IPv4) */
|
|
29
31
|
const PRIVATE_IP_RE = /^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.|127\.|0\.|169\.254\.|::1|fc|fd|fe80)/;
|
|
@@ -20,9 +20,6 @@ export class StatsCollector {
|
|
|
20
20
|
eventBus.subscribe('message:interrupted', (_event) => {
|
|
21
21
|
this.recordEvent({ type: 'interrupted', timestamp: Date.now() });
|
|
22
22
|
});
|
|
23
|
-
eventBus.subscribe('session:safe-mode-entered', (_event) => {
|
|
24
|
-
this.recordEvent({ type: 'safe-mode-entered', timestamp: Date.now() });
|
|
25
|
-
});
|
|
26
23
|
eventBus.subscribe('tool:result', (event) => {
|
|
27
24
|
const e = event;
|
|
28
25
|
if (e.isError) {
|
|
@@ -49,7 +46,6 @@ export class StatsCollector {
|
|
|
49
46
|
let toolErrors = 0;
|
|
50
47
|
const toolErrorsByName = {};
|
|
51
48
|
let interrupts = 0;
|
|
52
|
-
let safeModeEntries = 0;
|
|
53
49
|
let totalDuration = 0;
|
|
54
50
|
let durationCount = 0;
|
|
55
51
|
for (const event of this.events) {
|
|
@@ -79,9 +75,6 @@ export class StatsCollector {
|
|
|
79
75
|
case 'interrupted':
|
|
80
76
|
interrupts++;
|
|
81
77
|
break;
|
|
82
|
-
case 'safe-mode-entered':
|
|
83
|
-
safeModeEntries++;
|
|
84
|
-
break;
|
|
85
78
|
}
|
|
86
79
|
}
|
|
87
80
|
return {
|
|
@@ -94,7 +87,6 @@ export class StatsCollector {
|
|
|
94
87
|
toolErrors,
|
|
95
88
|
toolErrorsByName,
|
|
96
89
|
interrupts,
|
|
97
|
-
safeModeEntries,
|
|
98
90
|
avgResponseMs: durationCount > 0 ? totalDuration / durationCount : 0
|
|
99
91
|
}
|
|
100
92
|
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
请引导我完成 EvolClaw 的安装和配置。EvolClaw 是一个轻量级 AI Agent 网关,连接 Claude/Codex 到飞书、微信等 IM 通道。
|
|
2
|
+
|
|
3
|
+
**重要提示**:安装完成后,首次在 IM 渠道(飞书/微信)中与 EvolClaw 交互的用户将自动成为 owner,拥有完整管理权限。
|
|
4
|
+
|
|
5
|
+
按以下步骤执行:
|
|
6
|
+
|
|
7
|
+
## 第 1 步:环境检查
|
|
8
|
+
|
|
9
|
+
运行 `node --version` 和 `npm --version`,确认 Node.js >= 18、npm >= 8。
|
|
10
|
+
如果不符合要求,提示用户先升级 Node.js(https://nodejs.org)后停止。
|
|
11
|
+
|
|
12
|
+
## 第 2 步:确认安装
|
|
13
|
+
|
|
14
|
+
使用 AskUserQuestion 询问:
|
|
15
|
+
- 问题:是否立即安装 EvolClaw?
|
|
16
|
+
- 选项 A:立即安装
|
|
17
|
+
- 选项 B:仅显示命令,我稍后手动执行
|
|
18
|
+
|
|
19
|
+
如果选择 B,输出 `npm install -g evolclaw` 后结束。
|
|
20
|
+
|
|
21
|
+
## 第 3 步:执行安装
|
|
22
|
+
|
|
23
|
+
运行 `npm install -g evolclaw`,然后 `evolclaw --version` 验证。
|
|
24
|
+
|
|
25
|
+
如果失败且是权限问题,提示:
|
|
26
|
+
```
|
|
27
|
+
sudo npm install -g evolclaw
|
|
28
|
+
# 或配置 npm prefix(推荐):
|
|
29
|
+
npm config set prefix ~/.npm-global && export PATH=~/.npm-global/bin:$PATH
|
|
30
|
+
npm install -g evolclaw
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 第 4 步:初始化配置
|
|
34
|
+
|
|
35
|
+
运行 `evolclaw init`。
|
|
36
|
+
|
|
37
|
+
提示用户:EvolClaw 自动继承 Claude Code CLI 的 API Key,无需重复配置。
|
|
38
|
+
|
|
39
|
+
## 第 5 步:配置消息渠道
|
|
40
|
+
|
|
41
|
+
使用 AskUserQuestion(multiSelect: true)询问需要配置哪些渠道:
|
|
42
|
+
- 飞书:运行 `evolclaw init feishu`(扫码登录)
|
|
43
|
+
- 微信:运行 `evolclaw init wechat`(扫码登录)
|
|
44
|
+
- 暂不配置:跳过
|
|
45
|
+
|
|
46
|
+
## 第 6 步:启动并验证
|
|
47
|
+
|
|
48
|
+
运行 `evolclaw start`,再运行 `evolclaw status` 确认状态为 Running。
|
|
49
|
+
|
|
50
|
+
安装完成后告知用户:
|
|
51
|
+
- 在飞书/微信中发消息验证连接
|
|
52
|
+
- 发送 `/help` 查看所有命令
|
|
53
|
+
- 发送 `/bind <项目路径>` 绑定项目目录
|
|
54
|
+
- 常用命令:`evolclaw stop/restart/logs`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "evolclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"description": "Lightweight AI Agent gateway connecting Claude Agent SDK to messaging channels (Feishu, ACP) with multi-project session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -10,11 +10,12 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
"dist/",
|
|
12
12
|
"!dist/experimental/",
|
|
13
|
-
"data/evolclaw.sample.json"
|
|
13
|
+
"data/evolclaw.sample.json",
|
|
14
|
+
"evolclaw-install.md"
|
|
14
15
|
],
|
|
15
16
|
"scripts": {
|
|
16
17
|
"dev": "tsx watch src/index.ts",
|
|
17
|
-
"build": "tsc && node -e \"const f='dist/cli.js',c=require('fs').readFileSync(f,'utf8');if(!c.startsWith('#!'))require('fs').writeFileSync(f,'#!/usr/bin/env node\\n'+c)\" && node -e \"try{require('child_process').execFileSync('chmod',['+x','dist/cli.js'])}catch{}\"",
|
|
18
|
+
"build": "tsc && node -e \"const f='dist/cli.js',c=require('fs').readFileSync(f,'utf8');if(!c.startsWith('#!'))require('fs').writeFileSync(f,'#!/usr/bin/env node\\n'+c)\" && node -e \"try{require('child_process').execFileSync('chmod',['+x','dist/cli.js'])}catch{}\" && node -e \"require('fs').cpSync('src/templates','dist/templates',{recursive:true,force:true})\"",
|
|
18
19
|
"start": "node dist/index.js",
|
|
19
20
|
"test": "vitest run",
|
|
20
21
|
"test:watch": "vitest",
|
|
@@ -23,10 +24,16 @@
|
|
|
23
24
|
},
|
|
24
25
|
"dependencies": {
|
|
25
26
|
"@anthropic-ai/claude-agent-sdk": "^0.2.100",
|
|
26
|
-
"@eleans/aun-core-
|
|
27
|
+
"@eleans/aun-core-sdk": "^0.2.9",
|
|
27
28
|
"@larksuiteoapi/node-sdk": "^1.59.0",
|
|
28
29
|
"@openai/codex-sdk": "^0.118.0",
|
|
30
|
+
"@types/form-data": "^2.2.1",
|
|
31
|
+
"@wecom/aibot-node-sdk": "^1.0.6",
|
|
32
|
+
"dingtalk-stream": "^2.1.6-beta.1",
|
|
33
|
+
"fast-xml-parser": "^5.7.2",
|
|
34
|
+
"form-data": "^4.0.5",
|
|
29
35
|
"image-type": "^6.0.0",
|
|
36
|
+
"pure-qqbot": "^2.0.0",
|
|
30
37
|
"qrcode-terminal": "^0.12.0"
|
|
31
38
|
},
|
|
32
39
|
"devDependencies": {
|