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 +1 -1
- package/dist/config.js +2 -0
- package/dist/ipc.js +19 -11
- package/dist/paths.js +10 -1
- package/dist/utils/init-channel.js +63 -21
- package/dist/utils/init.js +21 -23
- package/package.json +1 -1
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
|
-
|
|
17
|
-
|
|
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
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
59
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
|
731
|
-
|
|
732
|
-
|
|
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
|
-
|
|
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
|
|
749
|
-
|
|
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
|
|
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();
|
package/dist/utils/init.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
424
|
-
|
|
425
|
-
|
|
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
|
-
|
|
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