evolclaw 2.6.0 → 2.6.2
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/channels/aun.js +127 -17
- package/dist/channels/feishu.js +4 -3
- package/dist/cli.js +104 -7
- package/dist/config.js +1 -1
- package/dist/core/command-handler.js +111 -8
- package/dist/core/message/message-processor.js +64 -50
- package/dist/core/message/message-queue.js +10 -2
- package/dist/core/message/stream-debouncer.js +9 -1
- package/dist/core/message/thought-emitter.js +153 -0
- package/dist/index.js +51 -30
- package/dist/templates/skills.md +5 -3
- package/dist/utils/init-channel.js +78 -5
- package/dist/utils/init.js +3 -3
- package/dist/utils/upgrade.js +100 -0
- package/package.json +3 -3
package/dist/channels/aun.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AUNClient, GatewayDiscovery } from '@agentunion/
|
|
1
|
+
import { AUNClient, GatewayDiscovery } from '@agentunion/fastaun';
|
|
2
2
|
import crypto from 'crypto';
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import path from 'path';
|
|
@@ -58,9 +58,16 @@ export class AUNChannel {
|
|
|
58
58
|
const logPath = path.join(resolvePaths().logs, `aun-${today}.log`);
|
|
59
59
|
this.traceStream = fs.createWriteStream(logPath, { flags: 'a' });
|
|
60
60
|
}
|
|
61
|
-
/** 判断 channelId 是否为群组 ID
|
|
61
|
+
/** 判断 channelId 是否为群组 ID
|
|
62
|
+
* - 新格式:group.{issuer}/{group_no|group_name}
|
|
63
|
+
* - 数字群号:{group_no}.{issuer}(如 11117.agentid.pub)
|
|
64
|
+
* - 兼容旧格式:grp_xxx、g-xxx.agentid.pub
|
|
65
|
+
*/
|
|
62
66
|
isGroupId(id) {
|
|
63
|
-
return id.startsWith('
|
|
67
|
+
return (id.startsWith('group.') && id.includes('/'))
|
|
68
|
+
|| /^\d+\./.test(id)
|
|
69
|
+
|| id.startsWith('grp_')
|
|
70
|
+
|| (id.startsWith('g-') && id.includes('.'));
|
|
64
71
|
}
|
|
65
72
|
getShortAid(aid) {
|
|
66
73
|
if (!aid)
|
|
@@ -114,6 +121,7 @@ export class AUNChannel {
|
|
|
114
121
|
this.messageSeqMap.delete(messageId);
|
|
115
122
|
}
|
|
116
123
|
_aid;
|
|
124
|
+
_selfName; // 本地 agent.md 中的 name,首次 connect 时读取
|
|
117
125
|
_chatId = ''; // aid:device_id:slot_id — 多实例回声过滤
|
|
118
126
|
seenMessages = new Map();
|
|
119
127
|
peerInfoCache = new Map();
|
|
@@ -262,6 +270,7 @@ export class AUNChannel {
|
|
|
262
270
|
this._aid = this.client.aid ?? undefined;
|
|
263
271
|
const deviceId = this.client._device_id ?? '';
|
|
264
272
|
this._chatId = this._aid ? `${this._aid}:${deviceId}:` : '';
|
|
273
|
+
this._selfName = this.loadSelfName(aidName);
|
|
265
274
|
this.connected = true;
|
|
266
275
|
this.reconnectAttempt = 0;
|
|
267
276
|
// Workaround: SDK e2ee uses _identity.cert for sender_cert_fingerprint;
|
|
@@ -317,7 +326,8 @@ export class AUNChannel {
|
|
|
317
326
|
if (ownerInfo.type !== null && ownerInfo.type !== 'human') {
|
|
318
327
|
logger.warn(`[AUN] Owner ${owner} type is "${ownerInfo.type}" (not human). Consider using a human AID as owner.`);
|
|
319
328
|
}
|
|
320
|
-
// Name:
|
|
329
|
+
// Name: prefer existing agent.md name if user has customized it,
|
|
330
|
+
// otherwise generate "{ownerName}的Evol助手 ({aidLabel})" for disambiguation
|
|
321
331
|
const ownerAidClean = owner.startsWith('@') ? owner.slice(1) : owner;
|
|
322
332
|
let ownerDisplayName;
|
|
323
333
|
if (ownerInfo.name) {
|
|
@@ -326,7 +336,18 @@ export class AUNChannel {
|
|
|
326
336
|
else {
|
|
327
337
|
ownerDisplayName = ownerAidClean.split('.')[0].slice(0, 12);
|
|
328
338
|
}
|
|
329
|
-
|
|
339
|
+
// Check if init wrote a meaningful name (vs just the aid first label default)
|
|
340
|
+
const currentNameMatch = frontmatter.match(/^name:\s*"?([^"\n]+)/m);
|
|
341
|
+
const currentName = currentNameMatch?.[1]?.trim();
|
|
342
|
+
const aidLabel = aidName.split('.')[0];
|
|
343
|
+
let agentDisplayName;
|
|
344
|
+
if (currentName && currentName !== aidLabel) {
|
|
345
|
+
// User or previous init set a custom name — keep it
|
|
346
|
+
agentDisplayName = currentName;
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
agentDisplayName = `${ownerDisplayName}的Evol助手 (${aidLabel})`;
|
|
350
|
+
}
|
|
330
351
|
// Generate new agent.md with proper fields
|
|
331
352
|
const newAgentMd = `---
|
|
332
353
|
aid: "${aid}"
|
|
@@ -376,7 +397,21 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
376
397
|
- 所有命令以 \`/\` 开头
|
|
377
398
|
|
|
378
399
|
现在,请先使用 \`/bind\` 命令绑定您的项目目录,然后就可以开始工作了!`;
|
|
379
|
-
|
|
400
|
+
// First contact with Owner races against Owner's async cert fetch from
|
|
401
|
+
// gateway PKI; a 3s pause lets the cert propagate. persist_required asks
|
|
402
|
+
// the gateway to durably store the message so Owner can recover it via
|
|
403
|
+
// pull if the initial E2EE push still arrives before the cert resolves.
|
|
404
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
405
|
+
if (!this.client) {
|
|
406
|
+
logger.warn('[AUN] Client disconnected before welcome message could be sent');
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
await this.client.call('message.send', {
|
|
410
|
+
to: owner,
|
|
411
|
+
payload: { type: 'text', text: welcomeText },
|
|
412
|
+
encrypt: true,
|
|
413
|
+
persist_required: true,
|
|
414
|
+
});
|
|
380
415
|
logger.info(`[AUN] Welcome message sent to owner: ${owner}`);
|
|
381
416
|
}
|
|
382
417
|
catch (e) {
|
|
@@ -531,11 +566,14 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
531
566
|
this.acknowledgeImmediately(messageId, seq);
|
|
532
567
|
return;
|
|
533
568
|
}
|
|
569
|
+
// dispatch_mode from server tells agent how to work in this group
|
|
570
|
+
const dispatchMode = msg.dispatch_mode ?? payload?.dispatch_mode ?? 'mention';
|
|
534
571
|
const mentionedSelf = this._aid
|
|
535
572
|
? (this.hasExplicitMention(text, this._aid) || payloadMentions.includes(this._aid))
|
|
536
573
|
: false;
|
|
537
574
|
const mentionedAll = this.hasExplicitMention(text, 'all') || payloadMentions.includes('all');
|
|
538
|
-
|
|
575
|
+
// In mention mode, only respond when explicitly mentioned; in broadcast mode, respond to all
|
|
576
|
+
if (dispatchMode === 'mention' && !mentionedSelf && !mentionedAll) {
|
|
539
577
|
this.acknowledgeImmediately(messageId, seq);
|
|
540
578
|
return;
|
|
541
579
|
}
|
|
@@ -550,7 +588,9 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
550
588
|
this.acknowledgeImmediately(messageId, seq);
|
|
551
589
|
return;
|
|
552
590
|
}
|
|
553
|
-
const mentions = mentionedAll
|
|
591
|
+
const mentions = mentionedAll
|
|
592
|
+
? ['all']
|
|
593
|
+
: mentionedSelf && this._aid ? [this._aid] : [];
|
|
554
594
|
// Process attachments
|
|
555
595
|
let finalText = strippedText;
|
|
556
596
|
if (hasAttachments && this.client) {
|
|
@@ -708,7 +748,8 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
708
748
|
const payload = { type: 'text', text: finalText };
|
|
709
749
|
if (context?.threadId)
|
|
710
750
|
payload.thread_id = context.threadId;
|
|
711
|
-
const
|
|
751
|
+
const isGroup = this.isGroupId(channelId);
|
|
752
|
+
const params = { payload, encrypt: !isGroup };
|
|
712
753
|
// Multi-instance routing: channelId may be "aid:device_id:slot_id"
|
|
713
754
|
const colonIdx = channelId.indexOf(':');
|
|
714
755
|
const targetAid = colonIdx > 0 ? channelId.substring(0, colonIdx) : channelId;
|
|
@@ -716,7 +757,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
716
757
|
params.payload.chat_id = channelId;
|
|
717
758
|
}
|
|
718
759
|
try {
|
|
719
|
-
if (
|
|
760
|
+
if (isGroup) {
|
|
720
761
|
params.group_id = channelId;
|
|
721
762
|
this.trace('OUT', 'group.send', params);
|
|
722
763
|
await this.client.call('group.send', params);
|
|
@@ -732,6 +773,50 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
732
773
|
logger.error(`[AUN] Send failed to ${channelId}: ${e}`);
|
|
733
774
|
}
|
|
734
775
|
}
|
|
776
|
+
/**
|
|
777
|
+
* 发送 thought 内容(Proactive 模式可观测)
|
|
778
|
+
* 群聊:调用 group.thought.put
|
|
779
|
+
* 单聊:调用 message.thought.put
|
|
780
|
+
*
|
|
781
|
+
* selector 使用 context: { type: 'task', id: taskId }
|
|
782
|
+
* 存储键:group_id/peer_aid + sender_aid + context.type + context.id
|
|
783
|
+
*/
|
|
784
|
+
async sendThought(channelId, taskId, payload) {
|
|
785
|
+
if (!this.connected || !this.client)
|
|
786
|
+
return;
|
|
787
|
+
if (!taskId)
|
|
788
|
+
return;
|
|
789
|
+
// Multi-instance routing
|
|
790
|
+
const colonIdx = channelId.indexOf(':');
|
|
791
|
+
const targetId = colonIdx > 0 ? channelId.substring(0, colonIdx) : channelId;
|
|
792
|
+
const params = {
|
|
793
|
+
context: { type: 'task', id: taskId },
|
|
794
|
+
payload,
|
|
795
|
+
encrypt: true,
|
|
796
|
+
};
|
|
797
|
+
try {
|
|
798
|
+
if (this.isGroupId(channelId)) {
|
|
799
|
+
params.group_id = targetId;
|
|
800
|
+
this.trace('OUT', 'group.thought.put', params);
|
|
801
|
+
await this.client.call('group.thought.put', params);
|
|
802
|
+
}
|
|
803
|
+
else {
|
|
804
|
+
params.to = targetId;
|
|
805
|
+
this.trace('OUT', 'message.thought.put', params);
|
|
806
|
+
await this.client.call('message.thought.put', params);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
catch (e) {
|
|
810
|
+
const err = e;
|
|
811
|
+
this.trace('OUT', 'thought.put.error', {
|
|
812
|
+
channelId,
|
|
813
|
+
errorName: err?.name,
|
|
814
|
+
errorCode: err?.code,
|
|
815
|
+
errorMessage: err?.message,
|
|
816
|
+
});
|
|
817
|
+
logger.debug(`[AUN] thought.put failed to ${channelId}: ${err?.name}(${err?.code})=${err?.message}`);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
735
820
|
async sendFile(channelId, filePath, context) {
|
|
736
821
|
if (!this.connected || !this.client) {
|
|
737
822
|
logger.warn('[AUN] Cannot sendFile: not connected');
|
|
@@ -805,14 +890,15 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
805
890
|
};
|
|
806
891
|
if (context?.threadId)
|
|
807
892
|
filePayload.thread_id = context.threadId;
|
|
808
|
-
const
|
|
893
|
+
const isGroup = this.isGroupId(channelId);
|
|
894
|
+
const params = { payload: filePayload, encrypt: !isGroup };
|
|
809
895
|
// Multi-instance routing
|
|
810
896
|
const fileColonIdx = channelId.indexOf(':');
|
|
811
897
|
const fileTargetAid = fileColonIdx > 0 ? channelId.substring(0, fileColonIdx) : channelId;
|
|
812
898
|
if (fileColonIdx > 0) {
|
|
813
899
|
params.payload.chat_id = channelId;
|
|
814
900
|
}
|
|
815
|
-
if (
|
|
901
|
+
if (isGroup) {
|
|
816
902
|
params.group_id = channelId;
|
|
817
903
|
this.trace('OUT', 'group.send.file', params);
|
|
818
904
|
await this.client.call('group.send', params);
|
|
@@ -834,7 +920,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
834
920
|
// to avoid duplicate "已送达" at the sender CLI
|
|
835
921
|
this.messageSeqMap.delete(messageId);
|
|
836
922
|
}
|
|
837
|
-
sendProcessingStatus(channelId, status, sessionId, context) {
|
|
923
|
+
sendProcessingStatus(channelId, status, sessionId, taskId, context) {
|
|
838
924
|
if (status === 'start')
|
|
839
925
|
this.sentCount.delete(channelId); // 新任务开始,重置计数
|
|
840
926
|
if (!this.client || !this.connected)
|
|
@@ -849,19 +935,20 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
849
935
|
const payload = {
|
|
850
936
|
type: 'event',
|
|
851
937
|
event: eventMap[status] ?? `task.${status}`,
|
|
852
|
-
data: { session_id: sessionId },
|
|
938
|
+
data: { task_id: taskId, session_id: sessionId },
|
|
853
939
|
severity: status === 'error' || status === 'timeout' ? 'error' : 'info',
|
|
854
940
|
};
|
|
855
941
|
if (context?.threadId)
|
|
856
942
|
payload.thread_id = context.threadId;
|
|
857
|
-
const
|
|
943
|
+
const isGroup = this.isGroupId(channelId);
|
|
944
|
+
const params = { payload, encrypt: !isGroup };
|
|
858
945
|
// Multi-instance routing
|
|
859
946
|
const statusColonIdx = channelId.indexOf(':');
|
|
860
947
|
const statusTargetAid = statusColonIdx > 0 ? channelId.substring(0, statusColonIdx) : channelId;
|
|
861
948
|
if (statusColonIdx > 0) {
|
|
862
949
|
payload.chat_id = channelId;
|
|
863
950
|
}
|
|
864
|
-
if (
|
|
951
|
+
if (isGroup) {
|
|
865
952
|
params.group_id = channelId;
|
|
866
953
|
this.trace('OUT', 'group.send.status', params);
|
|
867
954
|
this.client.call('group.send', params).catch(e => {
|
|
@@ -983,6 +1070,27 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
983
1070
|
maxAttempts: AUNChannel.RECONNECT_DELAYS.length,
|
|
984
1071
|
};
|
|
985
1072
|
}
|
|
1073
|
+
/** 读取本地 agent.md 中的 name(用于身份上下文展示) */
|
|
1074
|
+
loadSelfName(aid) {
|
|
1075
|
+
try {
|
|
1076
|
+
const aidName = aid.startsWith('@') ? aid.slice(1) : aid;
|
|
1077
|
+
const agentMdPath = path.join(os.homedir(), '.aun', 'AIDs', aidName, 'agent.md');
|
|
1078
|
+
if (!fs.existsSync(agentMdPath))
|
|
1079
|
+
return undefined;
|
|
1080
|
+
const content = fs.readFileSync(agentMdPath, 'utf-8');
|
|
1081
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
1082
|
+
if (!fmMatch)
|
|
1083
|
+
return undefined;
|
|
1084
|
+
const nameMatch = fmMatch[1].match(/^name:\s*["']?(.+?)["']?\s*$/m);
|
|
1085
|
+
return nameMatch?.[1]?.trim() || undefined;
|
|
1086
|
+
}
|
|
1087
|
+
catch {
|
|
1088
|
+
return undefined;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
getSelfName() {
|
|
1092
|
+
return this._selfName;
|
|
1093
|
+
}
|
|
986
1094
|
async fetchPeerInfo(aid) {
|
|
987
1095
|
const cached = this.peerInfoCache.get(aid);
|
|
988
1096
|
if (cached !== undefined)
|
|
@@ -1048,11 +1156,13 @@ export class AUNChannelPlugin {
|
|
|
1048
1156
|
sendText: (id, text, context) => channel.sendMessage(id, text, context),
|
|
1049
1157
|
sendFile: (id, filePath, context) => channel.sendFile(id, filePath, context),
|
|
1050
1158
|
acknowledge: (messageId) => { channel.acknowledge(messageId); return Promise.resolve(); },
|
|
1051
|
-
sendProcessingStatus: (id, status, sessionId, context) => channel.sendProcessingStatus(id, status, sessionId, context),
|
|
1159
|
+
sendProcessingStatus: (id, status, sessionId, taskId, context) => channel.sendProcessingStatus(id, status, sessionId, taskId, context),
|
|
1052
1160
|
sendCustomPayload: (id, payload) => channel.sendCustomPayload(id, payload),
|
|
1053
1161
|
uploadAgentMd: (content) => channel.uploadAgentMd(content),
|
|
1054
1162
|
downloadAgentMd: (aid) => channel.downloadAgentMd(aid),
|
|
1163
|
+
putThought: (id, taskId, payload) => channel.sendThought(id, taskId, payload),
|
|
1055
1164
|
_selfAid: () => channel.getStatus().aid,
|
|
1165
|
+
_selfName: () => channel.getSelfName(),
|
|
1056
1166
|
};
|
|
1057
1167
|
const policy = {
|
|
1058
1168
|
canSwitchProject: (chatType, identity) => identity === 'owner' || identity === 'admin',
|
package/dist/channels/feishu.js
CHANGED
|
@@ -499,11 +499,12 @@ export class FeishuChannel {
|
|
|
499
499
|
return;
|
|
500
500
|
try {
|
|
501
501
|
// 检测是否为图片,是则走 sendImage(内联预览)而非文件卡片
|
|
502
|
-
|
|
502
|
+
// 读取足够字节供 file-type 解析(ZIP-based 格式如 PPTX 需要更多字节)
|
|
503
|
+
const header = Buffer.alloc(4100);
|
|
503
504
|
const fd = fs.openSync(filePath, 'r');
|
|
504
|
-
fs.readSync(fd, header, 0,
|
|
505
|
+
const bytesRead = fs.readSync(fd, header, 0, 4100, 0);
|
|
505
506
|
fs.closeSync(fd);
|
|
506
|
-
const imgType = await imageType(header);
|
|
507
|
+
const imgType = await imageType(header.subarray(0, bytesRead)).catch(() => undefined);
|
|
507
508
|
if (imgType) {
|
|
508
509
|
logger.info(`[Feishu] Detected image (${imgType.mime}), sending as inline image:`, filePath);
|
|
509
510
|
const buf = fs.readFileSync(filePath);
|
package/dist/cli.js
CHANGED
|
@@ -12,6 +12,7 @@ import { ipcQuery } from './ipc.js';
|
|
|
12
12
|
import { cmdInitWechat, cmdInitFeishu, cmdInitAun, cmdInitDingtalk, cmdInitQQBot, cmdInitWecom } from './utils/init-channel.js';
|
|
13
13
|
import * as platform from './utils/cross-platform.js';
|
|
14
14
|
import { EventBus } from './core/event-bus.js';
|
|
15
|
+
import { tryUpgrade } from './utils/upgrade.js';
|
|
15
16
|
// Suppress Node.js ExperimentalWarning (e.g. SQLite) from cluttering CLI output
|
|
16
17
|
process.removeAllListeners('warning');
|
|
17
18
|
process.on('warning', (w) => { if (w.name === 'ExperimentalWarning')
|
|
@@ -372,6 +373,25 @@ async function cmdStop() {
|
|
|
372
373
|
async function cmdRestart() {
|
|
373
374
|
console.log('🔄 Restarting EvolClaw...');
|
|
374
375
|
const p = resolvePaths();
|
|
376
|
+
// 版本检查与自动升级
|
|
377
|
+
console.log('📦 Checking for updates...');
|
|
378
|
+
const upgrade = await tryUpgrade();
|
|
379
|
+
switch (upgrade.status) {
|
|
380
|
+
case 'upgraded':
|
|
381
|
+
console.log(`✅ Upgraded: ${upgrade.from} → ${upgrade.to}`);
|
|
382
|
+
break;
|
|
383
|
+
case 'no-update':
|
|
384
|
+
console.log(`✓ Already up to date (${upgrade.from})`);
|
|
385
|
+
break;
|
|
386
|
+
case 'skipped':
|
|
387
|
+
console.log(upgrade.error
|
|
388
|
+
? '⏭ Skipped upgrade (network unavailable)'
|
|
389
|
+
: '⏭ Skipped upgrade check (dev mode)');
|
|
390
|
+
break;
|
|
391
|
+
case 'failed':
|
|
392
|
+
console.log(`⚠ Upgrade failed (${upgrade.from} → ${upgrade.to}), continuing with current version`);
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
375
395
|
await stopAndWait(p.pid);
|
|
376
396
|
setTimeout(() => cmdStart(), 1000);
|
|
377
397
|
}
|
|
@@ -737,6 +757,27 @@ async function cmdRestartMonitor() {
|
|
|
737
757
|
});
|
|
738
758
|
await sleep(3000);
|
|
739
759
|
}
|
|
760
|
+
// 版本检查与自动升级
|
|
761
|
+
log('Checking for updates...');
|
|
762
|
+
const upgrade = await tryUpgrade();
|
|
763
|
+
switch (upgrade.status) {
|
|
764
|
+
case 'upgraded':
|
|
765
|
+
log(`✅ Upgraded: ${upgrade.from} → ${upgrade.to}`);
|
|
766
|
+
await notifyChannel(p, pendingInfo, `📦 已升级 ${upgrade.from} → ${upgrade.to}`, log);
|
|
767
|
+
break;
|
|
768
|
+
case 'no-update':
|
|
769
|
+
log(`Already up to date (${upgrade.from})`);
|
|
770
|
+
break;
|
|
771
|
+
case 'skipped':
|
|
772
|
+
log(upgrade.error
|
|
773
|
+
? 'Skipped upgrade (network unavailable)'
|
|
774
|
+
: 'Skipped upgrade check (dev mode)');
|
|
775
|
+
break;
|
|
776
|
+
case 'failed':
|
|
777
|
+
log(`⚠ Upgrade failed (${upgrade.from} → ${upgrade.to}): ${upgrade.error}`);
|
|
778
|
+
await notifyChannel(p, pendingInfo, `⚠️ 升级失败,使用当前版本继续`, log);
|
|
779
|
+
break;
|
|
780
|
+
}
|
|
740
781
|
// 启动并检测 ready signal
|
|
741
782
|
let started = await spawnAndWaitReady(p, log, READY_TIMEOUT);
|
|
742
783
|
if (started) {
|
|
@@ -1244,14 +1285,43 @@ async function cmdDiagnose() {
|
|
|
1244
1285
|
// ==================== Ctl ====================
|
|
1245
1286
|
async function cmdCtl(args) {
|
|
1246
1287
|
if (args.length === 0) {
|
|
1247
|
-
console.error(
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1288
|
+
console.error(`用法: evolclaw ctl <command> [args...]
|
|
1289
|
+
|
|
1290
|
+
查询:
|
|
1291
|
+
status 查看会话状态
|
|
1292
|
+
check 检查渠道健康状态
|
|
1293
|
+
help 显示帮助
|
|
1294
|
+
|
|
1295
|
+
配置:
|
|
1296
|
+
model [model-id] 查看/切换模型(如 opus, sonnet, haiku)
|
|
1297
|
+
effort [low|medium|high] 查看/切换推理强度
|
|
1298
|
+
compact 压缩当前会话上下文
|
|
1299
|
+
chatmode [mode] 查看/切换会话模式
|
|
1300
|
+
activity [all|dm|owner|none] 查看/控制中间输出显示模式
|
|
1301
|
+
perm [mode] 查看/切换权限模式
|
|
1302
|
+
|
|
1303
|
+
项目:
|
|
1304
|
+
bind <path> 注册项目目录(不切换当前会话)
|
|
1305
|
+
|
|
1306
|
+
消息:
|
|
1307
|
+
send <消息内容> 主动发送文本消息(proactive 模式)
|
|
1308
|
+
file [channel] <path> 发送项目内文件
|
|
1309
|
+
|
|
1310
|
+
运维:
|
|
1311
|
+
agentmd [put|set <内容>] 查看/管理 agent.md(仅 AUN 通道)
|
|
1312
|
+
restart [channel] 重启服务或重连指定渠道
|
|
1313
|
+
|
|
1314
|
+
示例:
|
|
1315
|
+
evolclaw ctl model sonnet
|
|
1316
|
+
evolclaw ctl effort high
|
|
1317
|
+
evolclaw ctl compact
|
|
1318
|
+
evolclaw ctl chatmode proactive`);
|
|
1253
1319
|
process.exit(1);
|
|
1254
1320
|
}
|
|
1321
|
+
// help 不需要连接服务,直接复用无参数时的帮助输出
|
|
1322
|
+
if (args[0] === 'help') {
|
|
1323
|
+
return cmdCtl([]);
|
|
1324
|
+
}
|
|
1255
1325
|
const sessionId = process.env.EVOLCLAW_SESSION_ID;
|
|
1256
1326
|
if (!sessionId) {
|
|
1257
1327
|
console.error('错误: EVOLCLAW_SESSION_ID 未设置(仅在 evolclaw 托管环境中可用)');
|
|
@@ -1294,7 +1364,32 @@ export async function main(args) {
|
|
|
1294
1364
|
}
|
|
1295
1365
|
switch (cmd) {
|
|
1296
1366
|
case 'init':
|
|
1297
|
-
if (args[1] === '
|
|
1367
|
+
if (args[1] === 'help') {
|
|
1368
|
+
console.log(`用法: evolclaw init [渠道] [选项]
|
|
1369
|
+
|
|
1370
|
+
交互式初始化:
|
|
1371
|
+
evolclaw init 创建基础配置文件(交互式)
|
|
1372
|
+
evolclaw init feishu 飞书扫码登录并写入配置
|
|
1373
|
+
evolclaw init wechat 微信扫码登录并写入配置
|
|
1374
|
+
evolclaw init dingtalk 钉钉扫码登录并写入配置
|
|
1375
|
+
evolclaw init qqbot QQ 机器人扫码绑定并写入配置
|
|
1376
|
+
evolclaw init wecom 企业微信 AI Bot 配置(手动输入)
|
|
1377
|
+
evolclaw init aun AUN 交互式配置(AID 创建 + Owner 绑定)
|
|
1378
|
+
|
|
1379
|
+
非交互式初始化:
|
|
1380
|
+
evolclaw init --non-interactive [选项]
|
|
1381
|
+
|
|
1382
|
+
选项:
|
|
1383
|
+
--default-path <path> 项目目录(默认: 当前目录)
|
|
1384
|
+
--channel <name> 渠道类型(默认: aun)
|
|
1385
|
+
--aun-aid <aid> AUN Agent ID(必填,如 mybot.agentid.pub)
|
|
1386
|
+
--aun-owner <aid> Owner AID(可选,如 alice.agentid.pub)
|
|
1387
|
+
|
|
1388
|
+
示例:
|
|
1389
|
+
evolclaw init --non-interactive --aun-aid mybot.agentid.pub --aun-owner alice.agentid.pub
|
|
1390
|
+
evolclaw init --non-interactive --default-path /home/user/project --aun-aid bot.agentid.pub`);
|
|
1391
|
+
}
|
|
1392
|
+
else if (args[1] === 'wechat') {
|
|
1298
1393
|
await cmdInitWechat();
|
|
1299
1394
|
}
|
|
1300
1395
|
else if (args[1] === 'feishu') {
|
|
@@ -1380,6 +1475,8 @@ Commands:
|
|
|
1380
1475
|
--level error|warn 只显示指定级别及以上
|
|
1381
1476
|
--module <name> 只显示指定模块(如 feishu、AgentRunner)
|
|
1382
1477
|
--raw 原始输出,不着色
|
|
1478
|
+
ctl 运行时自管理(模型切换、推理强度、压缩上下文等)
|
|
1479
|
+
evolclaw ctl help 查看完整命令列表
|
|
1383
1480
|
diagnose 诊断启动环境(配置、数据库、进程)
|
|
1384
1481
|
mv <old> <new> 迁移项目目录(保留 Claude/Codex/EvolClaw 会话)
|
|
1385
1482
|
|
package/dist/config.js
CHANGED
|
@@ -177,7 +177,7 @@ export function saveConfig(config, configPath = resolvePaths().config) {
|
|
|
177
177
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
178
178
|
}
|
|
179
179
|
// ── Channel instance normalization ──
|
|
180
|
-
export const channelTypes = ['feishu', 'wechat', 'aun', 'dingtalk', 'qqbot'];
|
|
180
|
+
export const channelTypes = ['feishu', 'wechat', 'aun', 'dingtalk', 'qqbot', 'wecom'];
|
|
181
181
|
/**
|
|
182
182
|
* Normalize a channel config value (single object, array, or undefined) into an array
|
|
183
183
|
* where every element has a `name` field.
|