evolclaw 2.5.6 → 2.5.8

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/dist/cli.js CHANGED
@@ -1307,7 +1307,7 @@ export async function main(args) {
1307
1307
  else if (args[1] === 'wecom') {
1308
1308
  await cmdInitWecom();
1309
1309
  }
1310
- else if (args[1]) {
1310
+ else if (args[1] && !args[1].startsWith('-')) {
1311
1311
  const supported = ['feishu', 'wechat', 'aun', 'dingtalk', 'qqbot', 'wecom'];
1312
1312
  console.error(`❌ 不支持的渠道: ${args[1]}`);
1313
1313
  console.error(` 支持的渠道: ${supported.join(', ')}`);
package/dist/config.js CHANGED
@@ -377,6 +377,8 @@ function validateConfig(config) {
377
377
  // Feishu 配置可选,但如果配置了就要完整(支持 array / object 两种格式)
378
378
  const feishuInstances = normalizeChannelInstances(config.channels?.feishu, 'feishu');
379
379
  for (const inst of feishuInstances) {
380
+ if (inst.enabled === false)
381
+ continue;
380
382
  const label = feishuInstances.length > 1 ? ` [${inst.name}]` : '';
381
383
  if (!inst.appId || inst.appId.startsWith('YOUR_')) {
382
384
  logger.warn(`⚠ Feishu${label} appId not configured (Feishu channel will be disabled)`);
package/dist/ipc.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import net from 'net';
2
2
  import fs from 'fs';
3
3
  import { logger } from './utils/logger.js';
4
+ const isWindows = process.platform === 'win32';
5
+ const isNamedPipe = (p) => isWindows && p.startsWith('\\\\.\\pipe\\');
4
6
  export class IpcServer {
5
7
  socketPath;
6
8
  getStatus;
@@ -12,11 +14,13 @@ export class IpcServer {
12
14
  this.commandExecutor = commandExecutor;
13
15
  }
14
16
  start() {
15
- // Remove stale socket file
16
- try {
17
- fs.unlinkSync(this.socketPath);
17
+ // Remove stale socket file (Unix only — named pipes auto-cleanup on process exit)
18
+ if (!isNamedPipe(this.socketPath)) {
19
+ try {
20
+ fs.unlinkSync(this.socketPath);
21
+ }
22
+ catch { }
18
23
  }
19
- catch { }
20
24
  this.server = net.createServer((conn) => {
21
25
  let buf = '';
22
26
  conn.on('data', async (data) => {
@@ -42,11 +46,13 @@ export class IpcServer {
42
46
  logger.error('[IPC] Server error:', err);
43
47
  });
44
48
  this.server.listen(this.socketPath, () => {
45
- // Ensure socket is readable by current user only
46
- try {
47
- fs.chmodSync(this.socketPath, 0o600);
49
+ // Restrict to current user (Unix only named pipes use Windows ACLs)
50
+ if (!isNamedPipe(this.socketPath)) {
51
+ try {
52
+ fs.chmodSync(this.socketPath, 0o600);
53
+ }
54
+ catch { }
48
55
  }
49
- catch { }
50
56
  logger.info(`[IPC] Listening on ${this.socketPath}`);
51
57
  });
52
58
  }
@@ -55,10 +61,12 @@ export class IpcServer {
55
61
  this.server.close();
56
62
  this.server = null;
57
63
  }
58
- try {
59
- fs.unlinkSync(this.socketPath);
64
+ if (!isNamedPipe(this.socketPath)) {
65
+ try {
66
+ fs.unlinkSync(this.socketPath);
67
+ }
68
+ catch { }
60
69
  }
61
- catch { }
62
70
  }
63
71
  async handleCommand(cmd) {
64
72
  switch (cmd.type) {
package/dist/paths.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
+ import crypto from 'crypto';
5
+ const isWindows = process.platform === 'win32';
4
6
  let _root = null;
5
7
  export function resolveRoot() {
6
8
  if (_root)
@@ -33,9 +35,16 @@ export function resolvePaths() {
33
35
  lineStats: path.join(root, 'logs', 'line-stats.log'),
34
36
  readySignal: path.join(root, 'logs', 'ready.signal'),
35
37
  selfHealLog: path.join(root, 'logs', 'self-heal.md'),
36
- socket: path.join(root, 'logs', 'evolclaw.sock'),
38
+ socket: resolveSocketPath(root),
37
39
  };
38
40
  }
41
+ function resolveSocketPath(root) {
42
+ if (isWindows) {
43
+ const hash = crypto.createHash('sha1').update(root).digest('hex').slice(0, 12);
44
+ return `\\\\.\\pipe\\evolclaw-${hash}`;
45
+ }
46
+ return path.join(root, 'logs', 'evolclaw.sock');
47
+ }
39
48
  export function ensureDataDirs() {
40
49
  const p = resolvePaths();
41
50
  fs.mkdirSync(p.dataDir, { recursive: true });
@@ -627,6 +627,44 @@ export function resolveAunCoreSdkPkg() {
627
627
  catch { /* not found */ }
628
628
  return null;
629
629
  }
630
+ /**
631
+ * Download AUN CA root certificate to ~/.aun/CA/root/root.crt.
632
+ * Idempotent: skips if file already exists. Returns true if a cert is on disk
633
+ * after the call (either pre-existing or freshly downloaded).
634
+ *
635
+ * Must be called BEFORE constructing any AUNClient that needs to verify
636
+ * gateway-issued certificates (e.g. for uploadAgentMd) — the SDK loads trusted
637
+ * roots from disk at client construction time and won't pick up later writes.
638
+ */
639
+ export async function downloadCaRoot(aunPath, gatewayUrl, indent = '') {
640
+ const caDir = path.join(aunPath, 'CA', 'root');
641
+ const caCertPath = path.join(caDir, 'root.crt');
642
+ if (fs.existsSync(caCertPath))
643
+ return true;
644
+ if (!gatewayUrl)
645
+ return false;
646
+ try {
647
+ fs.mkdirSync(caDir, { recursive: true });
648
+ const gwHttp = gatewayUrl.replace(/^wss?:/, 'https:').replace(/\/aun$/, '');
649
+ const resp = await fetch(`${gwHttp}/pki/chain`, { redirect: 'follow' });
650
+ if (!resp.ok) {
651
+ console.warn(`${indent}⚠ CA 根证书下载失败: HTTP ${resp.status}`);
652
+ return false;
653
+ }
654
+ const body = await resp.text();
655
+ if (!body.includes('BEGIN CERTIFICATE')) {
656
+ console.warn(`${indent}⚠ CA 根证书响应内容无效,跳过写入`);
657
+ return false;
658
+ }
659
+ fs.writeFileSync(caCertPath, body);
660
+ console.log(`${indent}✓ CA 根证书已下载`);
661
+ return true;
662
+ }
663
+ catch (e) {
664
+ console.warn(`${indent}⚠ CA 根证书下载失败: ${e},可稍后手动下载`);
665
+ return false;
666
+ }
667
+ }
630
668
  export async function checkAunEnvironment(rl) {
631
669
  console.log('\n🔍 AUN 环境检查...\n');
632
670
  const minVer = MIN_AUN_CORE_SDK.join('.');
@@ -718,7 +756,7 @@ export async function setupAunAid(rl, _config) {
718
756
  let failed = false;
719
757
  try {
720
758
  const { AUNClient } = await import('@agentunion/aun-node');
721
- const client = new AUNClient({ aun_path: aunPath });
759
+ let client = new AUNClient({ aun_path: aunPath });
722
760
  // 如果用户指定了自定义端口,手动设置 gateway URL;否则让 SDK 自动发现
723
761
  if (gatewayPort) {
724
762
  const domain = aid.split('.').slice(1).join('.');
@@ -727,26 +765,18 @@ export async function setupAunAid(rl, _config) {
727
765
  const result = await client.auth.createAid({ aid });
728
766
  console.log(` ✓ AID ${result.aid} 创建成功`);
729
767
  // 下载 CA 根证书(如果本地不存在),从 SDK 返回的实际网关 URL 派生
730
- const caDir = path.join(aunPath, 'CA', 'root');
731
- const caCertPath = path.join(caDir, 'root.crt');
732
- if (!fs.existsSync(caCertPath) && result.gateway) {
768
+ const caDownloaded = await downloadCaRoot(aunPath, result.gateway || '', ' ');
769
+ // 关键:CA 下载后必须重建 client,让 SDK 重新加载 trusted roots。
770
+ // 否则 uploadAgentMd 会因为 "no trusted roots available" 而失败。
771
+ if (caDownloaded) {
733
772
  try {
734
- fs.mkdirSync(caDir, { recursive: true });
735
- const gwHttp = result.gateway.replace(/^wss?:/, 'https:').replace(/\/aun$/, '');
736
- const resp = await fetch(`${gwHttp}/pki/chain`, { redirect: 'follow' });
737
- if (resp.ok) {
738
- const body = await resp.text();
739
- if (body.includes('BEGIN CERTIFICATE')) {
740
- fs.writeFileSync(caCertPath, body);
741
- console.log(' ✓ CA 根证书已下载');
742
- }
743
- else {
744
- console.warn(' ⚠ CA 根证书响应内容无效,跳过写入');
745
- }
746
- }
773
+ await client.close();
747
774
  }
748
- catch (e) {
749
- console.warn(` ⚠ CA 根证书下载失败: ${e},可稍后手动下载`);
775
+ catch { /* ignore */ }
776
+ client = new AUNClient({ aun_path: aunPath });
777
+ if (gatewayPort) {
778
+ const domain = aid.split('.').slice(1).join('.');
779
+ client._gatewayUrl = `wss://gateway.${domain}:${gatewayPort}/aun`;
750
780
  }
751
781
  }
752
782
  // Collect agent.md info and publish
@@ -754,13 +784,25 @@ export async function setupAunAid(rl, _config) {
754
784
  const agentType = typeInput === 'human' ? 'human' : 'ai';
755
785
  const agentName = aid.split('.')[0];
756
786
  const agentMdContent = `---\naid: "${aid}"\nname: "${agentName}"\ntype: "${agentType}"\nversion: "1.0.0"\ndescription: ""\ntags:\n - evolclaw\ninitialized: false\n---\n`;
787
+ const agentMdPath = path.join(aidDir, 'agent.md');
757
788
  try {
758
789
  await client.auth.uploadAgentMd(agentMdContent);
759
790
  console.log(' ✓ agent.md 已发布');
760
- fs.writeFileSync(path.join(aidDir, 'agent.md'), agentMdContent, 'utf-8');
761
791
  }
762
792
  catch (e) {
763
- console.log(` ⚠ agent.md 发布失败(可稍后用 /agentmd put 重试): ${String(e.message || e).slice(0, 100)}`);
793
+ console.log(` ⚠ agent.md 发布失败(首次连接将自动重试): ${String(e.message || e).slice(0, 100)}`);
794
+ }
795
+ fs.writeFileSync(agentMdPath, agentMdContent, 'utf-8');
796
+ if (!fs.existsSync(agentMdPath)) {
797
+ try {
798
+ await client.close();
799
+ }
800
+ catch { /* ignore */ }
801
+ console.log(` ✗ agent.md 本地写入校验失败: ${agentMdPath}`);
802
+ failed = true;
803
+ }
804
+ else {
805
+ console.log(' ✓ agent.md 已写入本地');
764
806
  }
765
807
  try {
766
808
  await client.close();
@@ -406,7 +406,7 @@ export async function cmdInit(options) {
406
406
  }
407
407
  if (options.channel === 'aun' && options.aunAid) {
408
408
  // 自动安装 AUN SDK
409
- const { resolveAunCoreSdkPkg, npmInstallGlobal } = await import('./init-channel.js');
409
+ const { resolveAunCoreSdkPkg, npmInstallGlobal, downloadCaRoot } = await import('./init-channel.js');
410
410
  if (!resolveAunCoreSdkPkg()) {
411
411
  console.log('正在安装 @agentunion/aun-node...');
412
412
  await npmInstallGlobal('@agentunion/aun-node@latest');
@@ -416,40 +416,38 @@ export async function cmdInit(options) {
416
416
  const aidDir = path.join(aunPath, 'AIDs', options.aunAid);
417
417
  if (!fs.existsSync(path.join(aidDir, 'private'))) {
418
418
  const { AUNClient } = await import('@agentunion/aun-node');
419
- const client = new AUNClient({ aun_path: aunPath });
419
+ let client = new AUNClient({ aun_path: aunPath });
420
420
  // 让 SDK 通过 well-known 自动发现网关
421
421
  const result = await client.auth.createAid({ aid: options.aunAid });
422
422
  // 下载 CA 根证书(如果本地不存在),从 SDK 返回的实际网关 URL 派生
423
- const caDir = path.join(aunPath, 'CA', 'root');
424
- const caCertPath = path.join(caDir, 'root.crt');
425
- if (!fs.existsSync(caCertPath) && result.gateway) {
423
+ const caDownloaded = await downloadCaRoot(aunPath, result.gateway || '');
424
+ // 关键:CA 下载后必须重建 client,让 SDK 重新加载 trusted roots。
425
+ // 否则 uploadAgentMd 会因为 "no trusted roots available" 而失败。
426
+ if (caDownloaded) {
426
427
  try {
427
- fs.mkdirSync(caDir, { recursive: true });
428
- const gwHttp = result.gateway.replace(/^wss?:/, 'https:').replace(/\/aun$/, '');
429
- const resp = await fetch(`${gwHttp}/pki/chain`, { redirect: 'follow' });
430
- if (resp.ok) {
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
- }
439
- }
440
- }
441
- catch (e) {
442
- console.warn(`⚠ CA 根证书下载失败: ${e},可稍后手动下载`);
428
+ await client.close();
443
429
  }
430
+ catch { }
431
+ client = new AUNClient({ aun_path: aunPath });
444
432
  }
445
433
  // 写入初始 agent.md(initialized: false)
446
434
  const agentName = options.aunAid.split('.')[0];
447
435
  const agentMd = `---\naid: "${options.aunAid}"\nname: "${agentName}"\ntype: "ai"\nversion: "1.0.0"\ndescription: ""\ntags:\n - evolclaw\ninitialized: false\n---\n`;
436
+ const agentMdPath = path.join(aidDir, 'agent.md');
448
437
  try {
449
438
  await client.auth.uploadAgentMd(agentMd);
450
- fs.writeFileSync(path.join(aidDir, 'agent.md'), agentMd, 'utf-8');
451
439
  }
452
- catch { }
440
+ catch (e) {
441
+ console.warn(`⚠ agent.md 网络发布失败(首次连接将自动重试): ${String(e?.message || e).slice(0, 100)}`);
442
+ }
443
+ fs.writeFileSync(agentMdPath, agentMd, 'utf-8');
444
+ if (!fs.existsSync(agentMdPath)) {
445
+ try {
446
+ await client.close();
447
+ }
448
+ catch { }
449
+ throw new Error(`agent.md 写入校验失败: ${agentMdPath}`);
450
+ }
453
451
  try {
454
452
  await client.close();
455
453
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evolclaw",
3
- "version": "2.5.6",
3
+ "version": "2.5.8",
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",