evolclaw 2.5.3 → 2.5.5

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.
@@ -10,7 +10,7 @@ import readline from 'readline';
10
10
  import path from 'path';
11
11
  import os from 'os';
12
12
  import crypto from 'crypto';
13
- import { createRequire } from 'module';
13
+ import { fileURLToPath } from 'url';
14
14
  import { execFile, execFileSync } from 'child_process';
15
15
  import { promisify } from 'util';
16
16
  import { resolvePaths } from '../paths.js';
@@ -20,8 +20,9 @@ import { isWindows } from './cross-platform.js';
20
20
  const execFileAsync = promisify(execFile);
21
21
  export async function npmInstallGlobal(pkg) {
22
22
  const npmCmd = isWindows ? 'npm.cmd' : 'npm';
23
+ const execOpts = { timeout: 180000, shell: isWindows };
23
24
  try {
24
- await execFileAsync(npmCmd, ['install', '-g', pkg], { timeout: 180000 });
25
+ await execFileAsync(npmCmd, ['install', '-g', pkg], execOpts);
25
26
  }
26
27
  catch (e) {
27
28
  if (e.stderr?.includes('EACCES') || e.message?.includes('EACCES')) {
@@ -563,9 +564,9 @@ export async function cmdInitWechat() {
563
564
  process.exit(1);
564
565
  }
565
566
  // ==================== AUN ====================
566
- // 最低 @eleans/aun-core-sdk 版本要求
567
- const MIN_AUN_CORE_SDK = [0, 2, 9];
568
- const AUN_CORE_SDK_PKG = '@eleans/aun-core-sdk';
567
+ // 最低 @agentunion/aun-node 版本要求
568
+ const MIN_AUN_CORE_SDK = [0, 2, 12];
569
+ const AUN_CORE_SDK_PKG = '@agentunion/aun-node';
569
570
  function compareVersion(a, min) {
570
571
  const parts = a.split('.').map(n => parseInt(n, 10));
571
572
  if (parts.length < 3 || parts.some(isNaN))
@@ -577,35 +578,31 @@ function compareVersion(a, min) {
577
578
  return parts[2] >= min[2];
578
579
  }
579
580
  export function resolveAunCoreSdkPkg() {
580
- // Strategy 1: createRequire (works when evolclaw and SDK share the same node_modules)
581
+ const pkgName = AUN_CORE_SDK_PKG;
582
+ // Strategy 1: walk up node_modules from this file (no require.resolve — avoids ESM exports issues)
581
583
  try {
582
- const esmRequire = createRequire(import.meta.url);
583
- const entry = esmRequire.resolve(AUN_CORE_SDK_PKG);
584
- const pkgPath = path.join(path.dirname(entry), 'package.json');
585
- if (!fs.existsSync(pkgPath)) {
586
- // 向上回溯查找 package.json
587
- let dir = path.dirname(entry);
588
- for (let i = 0; i < 5; i++) {
589
- const candidate = path.join(dir, 'package.json');
590
- if (fs.existsSync(candidate)) {
591
- const data = JSON.parse(fs.readFileSync(candidate, 'utf-8'));
592
- if (data.name === AUN_CORE_SDK_PKG)
593
- return { version: data.version, path: candidate };
594
- }
595
- dir = path.dirname(dir);
584
+ let dir = path.dirname(fileURLToPath(import.meta.url));
585
+ while (true) {
586
+ const candidate = path.join(dir, 'node_modules', pkgName, 'package.json');
587
+ if (fs.existsSync(candidate)) {
588
+ const data = JSON.parse(fs.readFileSync(candidate, 'utf-8'));
589
+ if (data.name === pkgName)
590
+ return { version: data.version, path: candidate };
596
591
  }
597
- }
598
- else {
599
- const data = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
600
- return { version: data.version, path: pkgPath };
592
+ const parent = path.dirname(dir);
593
+ if (parent === dir)
594
+ break;
595
+ dir = parent;
601
596
  }
602
597
  }
603
- catch { /* fall through to strategy 2 */ }
604
- // Strategy 2: npm root -g fallback (handles Windows / path mismatch)
598
+ catch { /* fall through */ }
599
+ // Strategy 2: npm root -g fallback (globally installed SDK)
605
600
  try {
606
601
  const npmCmd = isWindows ? 'npm.cmd' : 'npm';
607
- const globalRoot = execFileSync(npmCmd, ['root', '-g'], { encoding: 'utf-8', timeout: 10000 }).trim();
608
- const pkgPath = path.join(globalRoot, AUN_CORE_SDK_PKG, 'package.json');
602
+ const globalRoot = execFileSync(npmCmd, ['root', '-g'], {
603
+ encoding: 'utf-8', timeout: 10000, shell: isWindows,
604
+ }).trim();
605
+ const pkgPath = path.join(globalRoot, pkgName, 'package.json');
609
606
  if (fs.existsSync(pkgPath)) {
610
607
  const data = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
611
608
  return { version: data.version, path: pkgPath };
@@ -704,25 +701,32 @@ export async function setupAunAid(rl, _config) {
704
701
  console.log(' 正在创建 AID...');
705
702
  let failed = false;
706
703
  try {
707
- const { AUNClient } = await import('@eleans/aun-core-sdk');
704
+ const { AUNClient } = await import('@agentunion/aun-node');
708
705
  const client = new AUNClient({ aun_path: aunPath });
709
- // Set gateway URL from AID domain + port
710
- const domain = aid.split('.').slice(1).join('.');
711
- const port = gatewayPort || 443;
712
- client._gatewayUrl = `wss://gateway.${domain}:${port}/aun`;
706
+ // 如果用户指定了自定义端口,手动设置 gateway URL;否则让 SDK 自动发现
707
+ if (gatewayPort) {
708
+ const domain = aid.split('.').slice(1).join('.');
709
+ client._gatewayUrl = `wss://gateway.${domain}:${gatewayPort}/aun`;
710
+ }
713
711
  const result = await client.auth.createAid({ aid });
714
712
  console.log(` ✓ AID ${result.aid} 创建成功`);
715
- // 下载 CA 根证书(如果本地不存在)
713
+ // 下载 CA 根证书(如果本地不存在),从 SDK 返回的实际网关 URL 派生
716
714
  const caDir = path.join(aunPath, 'CA', 'root');
717
715
  const caCertPath = path.join(caDir, 'root.crt');
718
- if (!fs.existsSync(caCertPath)) {
716
+ if (!fs.existsSync(caCertPath) && result.gateway) {
719
717
  try {
720
718
  fs.mkdirSync(caDir, { recursive: true });
721
- // Derive HTTPS URL from gateway WebSocket URL (same as SDK's _gatewayHttpUrl)
722
- const resp = await fetch(`https://gateway.${domain}:${port}/pki/chain`);
719
+ const gwHttp = result.gateway.replace(/^wss?:/, 'https:').replace(/\/aun$/, '');
720
+ const resp = await fetch(`${gwHttp}/pki/chain`, { redirect: 'follow' });
723
721
  if (resp.ok) {
724
- fs.writeFileSync(caCertPath, await resp.text());
725
- console.log(' CA 根证书已下载');
722
+ const body = await resp.text();
723
+ if (body.includes('BEGIN CERTIFICATE')) {
724
+ fs.writeFileSync(caCertPath, body);
725
+ console.log(' ✓ CA 根证书已下载');
726
+ }
727
+ else {
728
+ console.warn(' ⚠ CA 根证书响应内容无效,跳过写入');
729
+ }
726
730
  }
727
731
  }
728
732
  catch (e) {
@@ -788,15 +792,29 @@ export async function cmdInitAun() {
788
792
  return;
789
793
  }
790
794
  const config = JSON.parse(fs.readFileSync(p.config, 'utf-8'));
791
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
792
- try {
793
- if (config.channels?.aun?.aid) {
794
- const answer = (await ask(rl, '已有 AUN 配置,是否重新配置?[y/N] ')).trim().toLowerCase();
795
- if (answer !== 'y' && answer !== 'yes') {
796
- console.log('已取消');
795
+ // Normalize existing instances and filter out placeholders
796
+ const allInstances = normalizeChannelInstances(config.channels?.aun, 'aun');
797
+ const validInstances = [];
798
+ for (let i = 0; i < allInstances.length; i++) {
799
+ const inst = allInstances[i];
800
+ if (!inst.aid || inst.aid.includes('your-') || inst.aid.includes('placeholder'))
801
+ continue;
802
+ validInstances.push({ ...inst, originalIndex: i });
803
+ }
804
+ let choice = null;
805
+ if (validInstances.length > 0) {
806
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
807
+ try {
808
+ choice = await selectInstance(rl, 'aun', validInstances);
809
+ if (choice === null)
797
810
  return;
798
- }
799
811
  }
812
+ finally {
813
+ rl.close();
814
+ }
815
+ }
816
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
817
+ try {
800
818
  if (!await checkAunEnvironment(rl)) {
801
819
  return;
802
820
  }
@@ -805,11 +823,43 @@ export async function cmdInitAun() {
805
823
  return;
806
824
  if (!config.channels)
807
825
  config.channels = {};
808
- config.channels.aun = {
809
- enabled: true,
810
- aid: result.aid,
811
- owner: result.owner,
812
- };
826
+ if (choice && choice.action === 'overwrite' && Array.isArray(config.channels.aun)) {
827
+ const idx = validInstances[choice.index]?.originalIndex ?? choice.index;
828
+ config.channels.aun[idx].aid = result.aid;
829
+ config.channels.aun[idx].owner = result.owner;
830
+ config.channels.aun[idx].enabled = true;
831
+ }
832
+ else if (choice && choice.action === 'overwrite' && !Array.isArray(config.channels.aun)) {
833
+ config.channels.aun = config.channels.aun || {};
834
+ config.channels.aun.aid = result.aid;
835
+ config.channels.aun.owner = result.owner;
836
+ config.channels.aun.enabled = true;
837
+ }
838
+ else if (choice && choice.action === 'add') {
839
+ const newInst = {
840
+ name: choice.name,
841
+ enabled: true,
842
+ aid: result.aid,
843
+ owner: result.owner,
844
+ };
845
+ if (Array.isArray(config.channels.aun)) {
846
+ config.channels.aun.push(newInst);
847
+ }
848
+ else if (config.channels.aun) {
849
+ const oldInst = { ...config.channels.aun, name: config.channels.aun.name || 'aun' };
850
+ config.channels.aun = [oldInst, newInst];
851
+ }
852
+ else {
853
+ config.channels.aun = [newInst];
854
+ }
855
+ }
856
+ else {
857
+ config.channels.aun = {
858
+ enabled: true,
859
+ aid: result.aid,
860
+ owner: result.owner,
861
+ };
862
+ }
813
863
  if (!config.channels.defaultChannel)
814
864
  config.channels.defaultChannel = 'aun';
815
865
  fs.writeFileSync(p.config, JSON.stringify(config, null, 2) + '\n');
@@ -408,28 +408,34 @@ export async function cmdInit(options) {
408
408
  // 自动安装 AUN SDK
409
409
  const { resolveAunCoreSdkPkg, npmInstallGlobal } = await import('./init-channel.js');
410
410
  if (!resolveAunCoreSdkPkg()) {
411
- console.log('正在安装 @eleans/aun-core-sdk...');
412
- await npmInstallGlobal('@eleans/aun-core-sdk@latest');
411
+ console.log('正在安装 @agentunion/aun-node...');
412
+ await npmInstallGlobal('@agentunion/aun-node@latest');
413
413
  }
414
414
  // 创建 AID(如果本地不存在)
415
415
  const aunPath = path.join(os.homedir(), '.aun');
416
416
  const aidDir = path.join(aunPath, 'AIDs', options.aunAid);
417
417
  if (!fs.existsSync(path.join(aidDir, 'private'))) {
418
- const { AUNClient } = await import('@eleans/aun-core-sdk');
418
+ const { AUNClient } = await import('@agentunion/aun-node');
419
419
  const client = new AUNClient({ aun_path: aunPath });
420
- const domain = options.aunAid.split('.').slice(1).join('.');
421
- client._gatewayUrl = `wss://gateway.${domain}:443/aun`;
422
- await client.auth.createAid({ aid: options.aunAid });
423
- // 下载 CA 根证书(如果本地不存在)
420
+ // SDK 通过 well-known 自动发现网关
421
+ const result = await client.auth.createAid({ aid: options.aunAid });
422
+ // 下载 CA 根证书(如果本地不存在),从 SDK 返回的实际网关 URL 派生
424
423
  const caDir = path.join(aunPath, 'CA', 'root');
425
424
  const caCertPath = path.join(caDir, 'root.crt');
426
- if (!fs.existsSync(caCertPath)) {
425
+ if (!fs.existsSync(caCertPath) && result.gateway) {
427
426
  try {
428
427
  fs.mkdirSync(caDir, { recursive: true });
429
- const resp = await fetch(`https://gateway.${domain}/pki/chain`);
428
+ const gwHttp = result.gateway.replace(/^wss?:/, 'https:').replace(/\/aun$/, '');
429
+ const resp = await fetch(`${gwHttp}/pki/chain`, { redirect: 'follow' });
430
430
  if (resp.ok) {
431
- fs.writeFileSync(caCertPath, await resp.text());
432
- console.log(' CA 根证书已下载');
431
+ const body = await resp.text();
432
+ if (body.includes('BEGIN CERTIFICATE')) {
433
+ fs.writeFileSync(caCertPath, body);
434
+ console.log('✓ CA 根证书已下载');
435
+ }
436
+ else {
437
+ console.warn('⚠ CA 根证书响应内容无效,跳过写入');
438
+ }
433
439
  }
434
440
  }
435
441
  catch (e) {
@@ -619,7 +625,10 @@ export async function selectInstance(rl, channelType, instances) {
619
625
  console.log(`\n发现已有 ${typeLabel} 机器人:`);
620
626
  const letters = 'abcdefghijklmnopqrstuvwxyz';
621
627
  for (let i = 0; i < instances.length; i++) {
622
- console.log(` ${letters[i]}. ${instances[i].name}`);
628
+ const inst = instances[i];
629
+ const id = inst.aid || inst.appId || inst.botId || inst.clientId || inst.token?.slice(0, 16) || '';
630
+ const suffix = id ? ` (${id})` : '';
631
+ console.log(` ${letters[i]}. ${inst.name}${suffix}`);
623
632
  }
624
633
  const addLetter = letters[instances.length];
625
634
  console.log(` ${addLetter}. 添加新机器人`);
@@ -50,11 +50,13 @@ npm install -g evolclaw
50
50
 
51
51
  | 参数 | 说明 | 示例 |
52
52
  |------|------|------|
53
- | `--aun-aid` | **必填**,你的 AUN Agent ID | `mybot.agentid.pub` |
54
- | `--aun-owner` | **必填**,Owner 的 AUN ID | `alice.agentid.pub` |
53
+ | `--aun-aid` | **必填**,Agent 自身的 AID,作为 bot 在 AUN 网络上的独立身份 | `mybot.agentid.pub` |
54
+ | `--aun-owner` | **必填**,Owner 的 AID,用于接收欢迎消息和管理权限 | `alice.agentid.pub` |
55
55
  | `--default-path` | 默认项目路径(可选,默认当前目录) | `/home/user/myproject` |
56
56
 
57
- 如果用户在触发安装时已提供 Owner AID(例如消息中包含"将 xxx 添加为 Owner ID"),直接使用该值,否则询问用户。
57
+ **注意**:`--aun-aid` `--aun-owner` 必须是不同的 AID。Agent 需要独立身份,不能与 Owner 相同。
58
+
59
+ 如果用户在触发安装时已提供 Owner AID(例如消息中包含"将 xxx 添加为 Owner ID"),直接使用该值,否则询问用户。Agent AID 必须单独询问用户,不能默认使用 Owner AID。
58
60
 
59
61
  ### 步骤 4:非交互式初始化
60
62
 
@@ -146,3 +148,111 @@ EvolClaw 首次连接 AUN 网络时自动:
146
148
  evolclaw restart
147
149
  evolclaw logs
148
150
  ```
151
+
152
+ **Q: 如何清理损坏的 AID 重新注册?**
153
+ ```bash
154
+ rm -rf ~/.aun/AIDs/<aid>
155
+ evolclaw init --non-interactive --channel aun --aun-aid <aid> --aun-owner <owner>
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Windows 故障排除(自动诊断)
161
+
162
+ 以下步骤面向 AI Agent 自动执行,按 if-then 格式编写。
163
+
164
+ ### evolclaw 命令不可用
165
+
166
+ 如果 `evolclaw --version` 返回 command not found 或无法识别:
167
+
168
+ **原因**:npm 全局 bin 目录不在系统 PATH 中。
169
+
170
+ **修复**:
171
+
172
+ ```bash
173
+ # 1. 获取 npm 全局 bin 目录
174
+ npm config get prefix
175
+ # Windows 输出示例:C:\Users\<user>\AppData\Roaming\npm
176
+ # Unix 输出示例:/usr/local/
177
+
178
+ # 2. 将该目录加入 PATH
179
+ # Windows (PowerShell, 当前用户永久生效):
180
+ [Environment]::SetEnvironmentVariable("Path", "$([Environment]::GetEnvironmentVariable('Path', 'User'));C:\Users\<user>\AppData\Roaming\npm", "User")
181
+ # Unix:
182
+ echo 'export PATH="$(npm config get prefix)/bin:$PATH"' >> ~/.bashrc && source ~/.bashrc
183
+
184
+ # 3. 验证
185
+ evolclaw --version
186
+ ```
187
+
188
+ ### SDK 找不到 Claude Code 可执行体
189
+
190
+ **症状**:`evolclaw start` 后日志出现 `spawn ENOENT`、`executable not found`、或 SDK 报 `Could not find claude-code executable`。
191
+
192
+ **原因**:Claude Code CLI 未安装,或安装后不在 PATH 中。
193
+
194
+ **修复**:
195
+
196
+ ```bash
197
+ # 1. 检查 claude 是否可用
198
+ # Windows:
199
+ where claude
200
+ # Unix:
201
+ which claude
202
+
203
+ # 2. 如果不存在,安装 Claude Code CLI
204
+ npm install -g @anthropic-ai/claude-code
205
+
206
+ # 3. 如果已安装但不在 PATH,同上一节方法将 npm 全局 bin 目录加入 PATH
207
+
208
+ # 4. 验证
209
+ claude --version
210
+ ```
211
+
212
+ 安装完成后 SDK 会自动发现 `claude` 可执行体,无需额外配置。
213
+
214
+ **备用方案**:如果因 SDK 平台包版本错位(win32-x64 二进制滞后于 SDK 主包)导致 SDK 内置可执行体不可用,但系统已有独立安装的 `claude`,可在 `~/.evolclaw/data/evolclaw.json` 中显式指定路径:
215
+
216
+ ```json
217
+ {
218
+ "agents": {
219
+ "anthropic": {
220
+ "pathToClaudeCodeExecutable": "C:\\Users\\<user>\\AppData\\Roaming\\npm\\claude.cmd"
221
+ }
222
+ }
223
+ }
224
+ ```
225
+
226
+ 此配置仅作为 SDK 自动发现失败时的 escape hatch。
227
+
228
+ ### SDK 平台包版本错位
229
+
230
+ **症状**:`npm install -g evolclaw` 成功,但启动时 SDK 报原生模块加载失败。
231
+
232
+ **诊断**:
233
+ ```bash
234
+ # 检查 SDK 主包版本
235
+ node -e "console.log(require('@anthropic-ai/claude-agent-sdk/package.json').version)"
236
+
237
+ # 检查平台包是否存在
238
+ node -e "try{require('@anthropic-ai/claude-code-win32-x64');console.log('OK')}catch(e){console.log('MISSING:',e.message)}"
239
+ ```
240
+
241
+ **修复**:确保系统有独立安装的 Claude Code CLI(`npm install -g @anthropic-ai/claude-code`),SDK 会优先使用 PATH 中的 `claude`。
242
+
243
+ ### npm install 报 spawn EINVAL
244
+
245
+ **症状**:`npm install -g evolclaw` 过程中报 `spawn EINVAL`。
246
+
247
+ **原因**:Node.js 24+ 的安全变更(CVE-2024-27980)与部分 npm 脚本不兼容。
248
+
249
+ **修复**:
250
+
251
+ ```bash
252
+ # 1. 升级到 evolclaw >= 2.5.4
253
+ npm install -g evolclaw@latest
254
+
255
+ # 2. 如仍报错,先手动安装 AUN SDK 依赖再执行 init
256
+ npm install -g @agentunion/aun-node
257
+ evolclaw init --non-interactive --channel aun --aun-aid <aid> --aun-owner <owner>
258
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evolclaw",
3
- "version": "2.5.3",
3
+ "version": "2.5.5",
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",
@@ -11,7 +11,8 @@
11
11
  "dist/",
12
12
  "!dist/experimental/",
13
13
  "data/evolclaw.sample.json",
14
- "evolclaw-install-aun.md"
14
+ "evolclaw-install-aun.md",
15
+ "aun/pyproject.toml"
15
16
  ],
16
17
  "scripts": {
17
18
  "dev": "tsx watch src/index.ts",
@@ -24,7 +25,7 @@
24
25
  },
25
26
  "dependencies": {
26
27
  "@anthropic-ai/claude-agent-sdk": "^0.2.100",
27
- "@eleans/aun-core-sdk": "^0.2.9",
28
+ "@agentunion/aun-node": "^0.2.12",
28
29
  "@larksuiteoapi/node-sdk": "^1.59.0",
29
30
  "@openai/codex-sdk": "^0.118.0",
30
31
  "@types/form-data": "^2.2.1",