evolclaw 3.3.0 → 3.4.0
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/CHANGELOG.md +36 -0
- package/README.md +7 -3
- package/dist/agents/claude-runner.js +23 -27
- package/dist/agents/codex-runner.js +90 -6
- package/dist/agents/runner-types.js +30 -0
- package/dist/aun/outbox.js +14 -2
- package/dist/channels/aun.js +506 -108
- package/dist/channels/feishu.js +29 -5
- package/dist/cli/agent-command.js +591 -0
- package/dist/cli/agent.js +15 -3
- package/dist/cli/aun-commands.js +1444 -0
- package/dist/cli/ctl-command.js +78 -0
- package/dist/cli/daemon-commands.js +2707 -0
- package/dist/cli/index.js +12 -5027
- package/dist/cli/restart-monitor.js +539 -0
- package/dist/cli/watch-logs.js +33 -0
- package/dist/core/channel-loader.js +4 -1
- package/dist/core/command/command-handler.js +1189 -0
- package/dist/core/command/menu-handler.js +1478 -0
- package/dist/core/command/slash-gate.js +142 -0
- package/dist/core/command/slash-handler.js +2090 -0
- package/dist/core/evolagent-registry.js +81 -0
- package/dist/core/evolagent.js +16 -0
- package/dist/core/message/im-renderer.js +67 -49
- package/dist/core/message/message-bridge.js +30 -9
- package/dist/core/message/message-processor.js +200 -122
- package/dist/core/message/message-queue.js +68 -0
- package/dist/core/permission.js +16 -0
- package/dist/core/session/session-manager.js +59 -13
- package/dist/core/stats/db.js +20 -0
- package/dist/core/stats/writer.js +3 -3
- package/dist/data/error-dict.json +7 -0
- package/dist/index.js +49 -6
- package/dist/ipc.js +99 -0
- package/dist/utils/cross-platform.js +35 -0
- package/dist/utils/ecweb-launch.js +49 -0
- package/dist/utils/error-utils.js +18 -5
- package/dist/utils/npm-ops.js +38 -8
- package/dist/utils/stats.js +63 -6
- package/kits/eck_manifest.json +0 -12
- package/package.json +2 -3
- package/dist/core/command-handler.js +0 -4235
- package/dist/core/message/response-depth.js +0 -56
- package/kits/templates/system-fragments/response-depth.md +0 -16
package/dist/channels/feishu.js
CHANGED
|
@@ -365,6 +365,8 @@ export class FeishuChannel {
|
|
|
365
365
|
}
|
|
366
366
|
catch (err) {
|
|
367
367
|
logger.error('[Feishu] Failed to handle card action:', err);
|
|
368
|
+
const detail = err instanceof Error && err.message ? err.message : String(err || '未知错误');
|
|
369
|
+
return { toast: { type: 'error', content: `❌ 操作失败: ${detail}` } };
|
|
368
370
|
}
|
|
369
371
|
},
|
|
370
372
|
});
|
|
@@ -1038,15 +1040,36 @@ export class FeishuChannel {
|
|
|
1038
1040
|
return false;
|
|
1039
1041
|
}
|
|
1040
1042
|
}
|
|
1041
|
-
|
|
1043
|
+
// messageId → Pin reaction create promise(等待 reaction_id 落地,供 promoteAck 删除)
|
|
1044
|
+
pinReactions = new Map();
|
|
1045
|
+
/** 收到消息时添加 Pin 表情(表示"已收到,排队中") */
|
|
1046
|
+
addPinReaction(messageId) {
|
|
1042
1047
|
if (!this.client)
|
|
1043
1048
|
return;
|
|
1049
|
+
const p = this.client.im.messageReaction.create({
|
|
1050
|
+
path: { message_id: messageId },
|
|
1051
|
+
data: { reaction_type: { emoji_type: 'Pin' } },
|
|
1052
|
+
}).then((res) => res?.data?.reaction_id)
|
|
1053
|
+
.catch(() => undefined);
|
|
1054
|
+
this.pinReactions.set(messageId, p);
|
|
1055
|
+
}
|
|
1056
|
+
/** Runner 开始执行时:添加 CheckMark,然后异步移除 Pin(视觉无闪烁) */
|
|
1057
|
+
async promoteAckReaction(messageId) {
|
|
1058
|
+
if (!this.client)
|
|
1059
|
+
return;
|
|
1060
|
+
// 先加 CheckMark,再删 Pin——用户看到的是 Pin→Pin+CheckMark→CheckMark,无空窗
|
|
1044
1061
|
this.client.im.messageReaction.create({
|
|
1045
1062
|
path: { message_id: messageId },
|
|
1046
|
-
data: {
|
|
1047
|
-
reaction_type: { emoji_type: 'CheckMark' }
|
|
1048
|
-
}
|
|
1063
|
+
data: { reaction_type: { emoji_type: 'CheckMark' } },
|
|
1049
1064
|
}).catch(() => { });
|
|
1065
|
+
const pending = this.pinReactions.get(messageId);
|
|
1066
|
+
this.pinReactions.delete(messageId);
|
|
1067
|
+
const reactionId = await pending;
|
|
1068
|
+
if (reactionId) {
|
|
1069
|
+
this.client.im.messageReaction.delete({
|
|
1070
|
+
path: { message_id: messageId, reaction_id: reactionId },
|
|
1071
|
+
}).catch(() => { });
|
|
1072
|
+
}
|
|
1050
1073
|
}
|
|
1051
1074
|
}
|
|
1052
1075
|
export class CardMetaStore {
|
|
@@ -1454,7 +1477,8 @@ export class FeishuChannelPlugin {
|
|
|
1454
1477
|
default: logger.warn(`[Feishu] Unhandled payload kind: ${payload.kind}`);
|
|
1455
1478
|
}
|
|
1456
1479
|
},
|
|
1457
|
-
acknowledge: (messageId) => { channel.
|
|
1480
|
+
acknowledge: (messageId) => { channel.addPinReaction(messageId); return Promise.resolve(); },
|
|
1481
|
+
promoteAck: (messageId) => channel.promoteAckReaction(messageId),
|
|
1458
1482
|
onInteraction: (callback) => channel.onInteraction(callback),
|
|
1459
1483
|
};
|
|
1460
1484
|
const policy = {
|
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { getArgValue, isHelpFlag, wantsHelp } from './help.js';
|
|
3
|
+
function formatTimeAgo(ms) {
|
|
4
|
+
const sec = Math.floor(ms / 1000);
|
|
5
|
+
if (sec < 60)
|
|
6
|
+
return '刚刚';
|
|
7
|
+
const min = Math.floor(sec / 60);
|
|
8
|
+
if (min < 60)
|
|
9
|
+
return `${min}分钟前`;
|
|
10
|
+
const hour = Math.floor(min / 60);
|
|
11
|
+
if (hour < 24)
|
|
12
|
+
return `${hour}小时前`;
|
|
13
|
+
const day = Math.floor(hour / 24);
|
|
14
|
+
return `${day}天前`;
|
|
15
|
+
}
|
|
16
|
+
function formatBytes(bytes) {
|
|
17
|
+
if (bytes < 1024)
|
|
18
|
+
return `${bytes}B`;
|
|
19
|
+
if (bytes < 1024 * 1024)
|
|
20
|
+
return `${(bytes / 1024).toFixed(1)}KB`;
|
|
21
|
+
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
|
|
22
|
+
}
|
|
23
|
+
// ==================== Agent ====================
|
|
24
|
+
export async function cmdAgent(args) {
|
|
25
|
+
const sub = args[0];
|
|
26
|
+
const formatJson = getArgValue(args, '--format') === 'json';
|
|
27
|
+
if (!sub || isHelpFlag(sub)) {
|
|
28
|
+
console.log(`用法: evolclaw agent <command>
|
|
29
|
+
|
|
30
|
+
Commands:
|
|
31
|
+
list 列出所有 agent
|
|
32
|
+
show <aid> 查看 agent 详情(身份 + 配置 + 连接 + 会话 + 路径)
|
|
33
|
+
new [aid] 交互式创建 agent
|
|
34
|
+
new <aid> --non-interactive ... 非交互式创建
|
|
35
|
+
enable <aid> 启用 agent
|
|
36
|
+
disable <aid> 停用 agent
|
|
37
|
+
get <aid> <key> 读取单个配置字段(支持点路径)
|
|
38
|
+
set <aid> <key> <val> 修改单个配置字段(支持点路径)
|
|
39
|
+
rename <aid> <name> 修改 agent 名称(更新 agent.md 并重新上传)
|
|
40
|
+
reload [aid] 热重载配置(无参数=全量 resync)
|
|
41
|
+
delete <aid> [--purge] 删除 agent
|
|
42
|
+
|
|
43
|
+
Options:
|
|
44
|
+
--format json 输出 JSON 格式
|
|
45
|
+
--help, -h 各子命令均支持,查看详细用法
|
|
46
|
+
|
|
47
|
+
示例:
|
|
48
|
+
evolclaw agent list
|
|
49
|
+
evolclaw agent show mybot.agentid.pub
|
|
50
|
+
evolclaw agent new mybot.agentid.pub
|
|
51
|
+
evolclaw agent enable mybot.agentid.pub
|
|
52
|
+
evolclaw agent get mybot.agentid.pub active_baseagent
|
|
53
|
+
evolclaw agent set mybot.agentid.pub active_baseagent codex
|
|
54
|
+
evolclaw agent rename mybot.agentid.pub "My Bot"
|
|
55
|
+
evolclaw agent delete mybot.agentid.pub --purge`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const { agentList, agentShow, agentCreateInteractive, agentCreateNonInteractive, agentReload, agentEnable, agentDisable, agentGet, agentSet, agentDelete, agentRename, } = await import('./agent.js');
|
|
59
|
+
// --- list ---
|
|
60
|
+
if (sub === 'list') {
|
|
61
|
+
if (wantsHelp(args)) {
|
|
62
|
+
console.log(`用法: evolclaw agent list [--format json]
|
|
63
|
+
|
|
64
|
+
列出所有 agent,显示名称、状态、渠道、项目、基座、最后活跃时间。`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const result = await agentList();
|
|
68
|
+
if (!result.ok) {
|
|
69
|
+
if (formatJson) {
|
|
70
|
+
console.log(JSON.stringify(result));
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
console.error(`❌ ${result.error}`);
|
|
74
|
+
}
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
if (formatJson) {
|
|
78
|
+
console.log(JSON.stringify(result, null, 2));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (result.agents.length === 0) {
|
|
82
|
+
console.log('No agents configured.');
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// 表头跟随系统语言
|
|
86
|
+
const isChinese = (process.env.LANG || process.env.LC_ALL || process.env.LANGUAGE || Intl.DateTimeFormat().resolvedOptions().locale || '').toLowerCase().includes('zh');
|
|
87
|
+
const headers = isChinese
|
|
88
|
+
? { name: '名称', status: '状态', channels: '渠道', project: '项目', baseagent: '基座', lastActive: '最后活跃' }
|
|
89
|
+
: { name: 'NAME', status: 'STATUS', channels: 'CHANNELS', project: 'PROJECT', baseagent: 'BASEAGENT', lastActive: 'LAST ACTIVE' };
|
|
90
|
+
// 计算各列实际需要的宽度
|
|
91
|
+
let maxNameLen = headers.name.length;
|
|
92
|
+
let maxStatusLen = headers.status.length;
|
|
93
|
+
let maxChannelsLen = headers.channels.length;
|
|
94
|
+
let maxProjectLen = headers.project.length;
|
|
95
|
+
let maxBaseagentLen = headers.baseagent.length;
|
|
96
|
+
for (const info of result.agents) {
|
|
97
|
+
maxNameLen = Math.max(maxNameLen, info.name.length);
|
|
98
|
+
maxStatusLen = Math.max(maxStatusLen, (info.status || 'stopped').length);
|
|
99
|
+
const channelsStr = info.channels?.length > 0 ? info.channels.join(', ') : '—';
|
|
100
|
+
maxChannelsLen = Math.max(maxChannelsLen, channelsStr.length);
|
|
101
|
+
const projectStr = info.projectPath ? path.basename(info.projectPath) : '—';
|
|
102
|
+
maxProjectLen = Math.max(maxProjectLen, projectStr.length);
|
|
103
|
+
maxBaseagentLen = Math.max(maxBaseagentLen, (info.baseagent || '—').length);
|
|
104
|
+
}
|
|
105
|
+
// 加 2 作为列间距
|
|
106
|
+
const colName = maxNameLen + 2;
|
|
107
|
+
const colStatus = maxStatusLen + 2;
|
|
108
|
+
const colChannels = maxChannelsLen + 1;
|
|
109
|
+
const colProject = maxProjectLen + 2;
|
|
110
|
+
const colBaseagent = maxBaseagentLen + 2;
|
|
111
|
+
console.log(headers.name.padEnd(colName) + headers.status.padEnd(colStatus) + headers.channels.padEnd(colChannels) +
|
|
112
|
+
headers.project.padEnd(colProject) + headers.baseagent.padEnd(colBaseagent) + headers.lastActive);
|
|
113
|
+
for (const info of result.agents) {
|
|
114
|
+
const name = info.name;
|
|
115
|
+
const status = info.status || 'stopped';
|
|
116
|
+
const channels = info.channels?.length > 0 ? info.channels.join(', ') : '—';
|
|
117
|
+
const project = info.projectPath ? path.basename(info.projectPath) : '—';
|
|
118
|
+
const baseagent = info.baseagent || '—';
|
|
119
|
+
const lastActive = info.lastActivity ? formatTimeAgo(Date.now() - info.lastActivity) : '—';
|
|
120
|
+
console.log(name.padEnd(colName) + status.padEnd(colStatus) + channels.padEnd(colChannels) +
|
|
121
|
+
project.padEnd(colProject) + baseagent.padEnd(colBaseagent) + lastActive);
|
|
122
|
+
}
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// --- new ---
|
|
126
|
+
if (sub === 'new') {
|
|
127
|
+
if (wantsHelp(args)) {
|
|
128
|
+
console.log(`用法: evolclaw agent new [aid] 交互式创建
|
|
129
|
+
evolclaw agent new <aid> --non-interactive [选项]
|
|
130
|
+
|
|
131
|
+
非交互模式选项:
|
|
132
|
+
--baseagent <claude|codex|gemini> 默认: PATH 中第一个可用
|
|
133
|
+
--project <absolute path> 必填
|
|
134
|
+
--owner <aid>
|
|
135
|
+
--name <display-name>
|
|
136
|
+
--description <text>
|
|
137
|
+
--force 覆盖已有 config.json
|
|
138
|
+
--format json 输出 JSON
|
|
139
|
+
|
|
140
|
+
示例:
|
|
141
|
+
evolclaw agent new mybot.agentid.pub
|
|
142
|
+
evolclaw agent new mybot.agentid.pub --non-interactive --project /abs/path --baseagent claude`);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const name = args[1];
|
|
146
|
+
const nonInteractive = args.includes('--non-interactive');
|
|
147
|
+
if (nonInteractive) {
|
|
148
|
+
if (!name) {
|
|
149
|
+
console.error('Usage: evolclaw agent new <aid> --non-interactive ...');
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
const getArg = (flag) => {
|
|
153
|
+
const idx = args.indexOf(flag);
|
|
154
|
+
return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : undefined;
|
|
155
|
+
};
|
|
156
|
+
const result = await agentCreateNonInteractive({
|
|
157
|
+
aid: name,
|
|
158
|
+
baseagent: getArg('--baseagent'),
|
|
159
|
+
project: getArg('--project') || '',
|
|
160
|
+
owner: getArg('--owner'),
|
|
161
|
+
name: getArg('--name'),
|
|
162
|
+
description: getArg('--description'),
|
|
163
|
+
force: args.includes('--force'),
|
|
164
|
+
});
|
|
165
|
+
if (!result.ok) {
|
|
166
|
+
if (formatJson) {
|
|
167
|
+
console.log(JSON.stringify(result));
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
console.error(`❌ ${result.error}`);
|
|
171
|
+
}
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
if (formatJson) {
|
|
175
|
+
console.log(JSON.stringify(result, null, 2));
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
console.log(`✓ Created: ${result.configPath}`);
|
|
179
|
+
console.log(result.agentmdUploaded
|
|
180
|
+
? ' ✓ agent.md 已发布'
|
|
181
|
+
: ' ⚠ agent.md 上传失败(可用 evolclaw aid agentmd put 重试)');
|
|
182
|
+
console.log(result.hotLoaded
|
|
183
|
+
? ' ✓ 已热重载,agent 已上线'
|
|
184
|
+
: result.hotLoadError
|
|
185
|
+
? ` ✗ 热重载失败:${result.hotLoadError}`
|
|
186
|
+
: ' ⚠ 服务未运行,下次 evolclaw start 时生效');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
const result = await agentCreateInteractive({ suggestedName: name });
|
|
191
|
+
if (!result.ok) {
|
|
192
|
+
if (formatJson) {
|
|
193
|
+
console.log(JSON.stringify(result));
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
console.error(`❌ ${result.error}`);
|
|
197
|
+
}
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
if (formatJson) {
|
|
201
|
+
console.log(JSON.stringify(result, null, 2));
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
console.log(`\n✓ Created: ${result.configPath}`);
|
|
205
|
+
console.log(result.agentmdUploaded
|
|
206
|
+
? ' ✓ agent.md 已发布'
|
|
207
|
+
: ' ⚠ agent.md 上传失败(可用 evolclaw aid agentmd put 重试)');
|
|
208
|
+
console.log(result.hotLoaded
|
|
209
|
+
? ' ✓ 已热重载,agent 已上线'
|
|
210
|
+
: result.hotLoadError
|
|
211
|
+
? ` ✗ 热重载失败:${result.hotLoadError}`
|
|
212
|
+
: ' ⚠ 服务未运行,下次 evolclaw start 时生效');
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
// --- sync-aids (deprecated, commented out) ---
|
|
218
|
+
// if (sub === 'sync-aids') {
|
|
219
|
+
// const result = await agentSyncAids();
|
|
220
|
+
// if (!result.ok) {
|
|
221
|
+
// if (formatJson) { console.log(JSON.stringify(result)); }
|
|
222
|
+
// else { console.error(`❌ ${result.error}`); }
|
|
223
|
+
// process.exit(1);
|
|
224
|
+
// }
|
|
225
|
+
// if (formatJson) {
|
|
226
|
+
// console.log(JSON.stringify(result, null, 2));
|
|
227
|
+
// return;
|
|
228
|
+
// }
|
|
229
|
+
// if (result.created.length === 0) {
|
|
230
|
+
// console.log('所有本地 AID 都已有对应 agent,无需同步。');
|
|
231
|
+
// } else {
|
|
232
|
+
// console.log(`✓ 同步完成:新建 ${result.created.length} 个 agent(模板: ${result.template})`);
|
|
233
|
+
// for (const aid of result.created) console.log(` ✓ ${aid}`);
|
|
234
|
+
// if (result.hotReloaded) console.log(' ✓ 已热加载到运行中的进程');
|
|
235
|
+
// else console.log(' evolclaw 未运行,新 agent 将在下次启动时加载。');
|
|
236
|
+
// }
|
|
237
|
+
// return;
|
|
238
|
+
// }
|
|
239
|
+
// --- reload ---
|
|
240
|
+
if (sub === 'reload') {
|
|
241
|
+
if (wantsHelp(args)) {
|
|
242
|
+
console.log(`用法: evolclaw agent reload [aid] [--format json]
|
|
243
|
+
|
|
244
|
+
热重载 agent 配置。
|
|
245
|
+
无参数 全量 resync(扫磁盘,新增上线、删除下线、修改热更新)
|
|
246
|
+
<aid> 仅热重载指定 agent`);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const target = args[1] && !args[1].startsWith('--') ? args[1] : undefined;
|
|
250
|
+
const result = await agentReload(target);
|
|
251
|
+
if (!result.ok) {
|
|
252
|
+
if (formatJson) {
|
|
253
|
+
console.log(JSON.stringify(result));
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
console.error(`✗ ${result.error}`);
|
|
257
|
+
}
|
|
258
|
+
process.exit(1);
|
|
259
|
+
}
|
|
260
|
+
if (formatJson) {
|
|
261
|
+
console.log(JSON.stringify(result, null, 2));
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (target) {
|
|
265
|
+
console.log(`✓ Agent "${target}" reloaded`);
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
console.log('✓ Agent resync 完成:');
|
|
269
|
+
for (const line of (result.results || []))
|
|
270
|
+
console.log(` ${line}`);
|
|
271
|
+
}
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
// --- enable ---
|
|
275
|
+
if (sub === 'enable') {
|
|
276
|
+
if (wantsHelp(args)) {
|
|
277
|
+
console.log(`用法: evolclaw agent enable <aid> [--format json]
|
|
278
|
+
|
|
279
|
+
启用 agent。若服务运行中会热重载,否则下次 evolclaw start 时生效。`);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const aid = args[1];
|
|
283
|
+
if (!aid) {
|
|
284
|
+
console.error('用法: evolclaw agent enable <aid>');
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
const result = await agentEnable(aid);
|
|
288
|
+
if (!result.ok) {
|
|
289
|
+
if (formatJson) {
|
|
290
|
+
console.log(JSON.stringify(result));
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
console.error(`❌ ${result.error}`);
|
|
294
|
+
}
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
if (formatJson) {
|
|
298
|
+
console.log(JSON.stringify(result, null, 2));
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
console.log(`✓ ${aid} enabled${result.reloaded ? ' (hot-reloaded)' : ''}`);
|
|
302
|
+
}
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
// --- disable ---
|
|
306
|
+
if (sub === 'disable') {
|
|
307
|
+
if (wantsHelp(args)) {
|
|
308
|
+
console.log(`用法: evolclaw agent disable <aid> [--format json]
|
|
309
|
+
|
|
310
|
+
停用 agent。若服务运行中会热重载离线,否则在配置中标记为禁用。`);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const aid = args[1];
|
|
314
|
+
if (!aid) {
|
|
315
|
+
console.error('用法: evolclaw agent disable <aid>');
|
|
316
|
+
process.exit(1);
|
|
317
|
+
}
|
|
318
|
+
const result = await agentDisable(aid);
|
|
319
|
+
if (!result.ok) {
|
|
320
|
+
if (formatJson) {
|
|
321
|
+
console.log(JSON.stringify(result));
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
console.error(`❌ ${result.error}`);
|
|
325
|
+
}
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
if (formatJson) {
|
|
329
|
+
console.log(JSON.stringify(result, null, 2));
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
console.log(`✓ ${aid} disabled${result.reloaded ? ' (hot-reloaded)' : ''}`);
|
|
333
|
+
}
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
// --- get ---
|
|
337
|
+
if (sub === 'get') {
|
|
338
|
+
if (wantsHelp(args)) {
|
|
339
|
+
console.log(`用法: evolclaw agent get <aid> <key> [--format json]
|
|
340
|
+
|
|
341
|
+
读取单个配置字段。key 支持点路径,如 "channels.aun.enabled"。
|
|
342
|
+
|
|
343
|
+
示例:
|
|
344
|
+
evolclaw agent get mybot.agentid.pub active_baseagent
|
|
345
|
+
evolclaw agent get mybot.agentid.pub channels.aun.enabled`);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
const aid = args[1];
|
|
349
|
+
const key = args[2];
|
|
350
|
+
if (!aid || !key) {
|
|
351
|
+
console.error('用法: evolclaw agent get <aid> <key>');
|
|
352
|
+
process.exit(1);
|
|
353
|
+
}
|
|
354
|
+
const result = await agentGet(aid, key);
|
|
355
|
+
if (!result.ok) {
|
|
356
|
+
if (formatJson) {
|
|
357
|
+
console.log(JSON.stringify(result));
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
console.error(`❌ ${result.error}`);
|
|
361
|
+
}
|
|
362
|
+
process.exit(1);
|
|
363
|
+
}
|
|
364
|
+
if (formatJson) {
|
|
365
|
+
console.log(JSON.stringify(result, null, 2));
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
const val = result.value;
|
|
369
|
+
console.log(typeof val === 'object' ? JSON.stringify(val, null, 2) : String(val));
|
|
370
|
+
}
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
// --- set ---
|
|
374
|
+
if (sub === 'set') {
|
|
375
|
+
if (wantsHelp(args)) {
|
|
376
|
+
console.log(`用法: evolclaw agent set <aid> <key> <value> [--format json]
|
|
377
|
+
|
|
378
|
+
修改单个配置字段。key 支持点路径。修改后若服务运行中会自动热重载。
|
|
379
|
+
|
|
380
|
+
示例:
|
|
381
|
+
evolclaw agent set mybot.agentid.pub active_baseagent codex
|
|
382
|
+
evolclaw agent set mybot.agentid.pub channels.aun.enabled true`);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
const aid = args[1];
|
|
386
|
+
const key = args[2];
|
|
387
|
+
const val = args[3];
|
|
388
|
+
if (!aid || !key || val === undefined) {
|
|
389
|
+
console.error('用法: evolclaw agent set <aid> <key> <value>');
|
|
390
|
+
process.exit(1);
|
|
391
|
+
}
|
|
392
|
+
const result = await agentSet(aid, key, val);
|
|
393
|
+
if (!result.ok) {
|
|
394
|
+
if (formatJson) {
|
|
395
|
+
console.log(JSON.stringify(result));
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
console.error(`❌ ${result.error}`);
|
|
399
|
+
}
|
|
400
|
+
process.exit(1);
|
|
401
|
+
}
|
|
402
|
+
if (formatJson) {
|
|
403
|
+
console.log(JSON.stringify(result, null, 2));
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
console.log(`✓ ${aid} ${key} = ${JSON.stringify(result.value)}${result.reloaded ? ' (hot-reloaded)' : ''}`);
|
|
407
|
+
}
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
// --- rename ---
|
|
411
|
+
if (sub === 'rename') {
|
|
412
|
+
if (wantsHelp(args)) {
|
|
413
|
+
console.log(`用法: evolclaw agent rename <aid> <name> [--format json]
|
|
414
|
+
|
|
415
|
+
修改 agent 显示名称。同时更新本地 agent.md 并尝试重新上传。
|
|
416
|
+
|
|
417
|
+
示例:
|
|
418
|
+
evolclaw agent rename mybot.agentid.pub "My Bot"`);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const aid = args[1];
|
|
422
|
+
const newName = args[2];
|
|
423
|
+
if (!aid || !newName) {
|
|
424
|
+
console.error('用法: evolclaw agent rename <aid> <name>');
|
|
425
|
+
process.exit(1);
|
|
426
|
+
}
|
|
427
|
+
const result = await agentRename(aid, newName);
|
|
428
|
+
if (!result.ok) {
|
|
429
|
+
if (formatJson) {
|
|
430
|
+
console.log(JSON.stringify(result));
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
console.error(`❌ ${result.error}`);
|
|
434
|
+
}
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
if (formatJson) {
|
|
438
|
+
console.log(JSON.stringify(result, null, 2));
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
console.log(`✓ ${aid} renamed to "${newName}"${result.uploaded ? ' (uploaded)' : ' (local only, upload failed)'}`);
|
|
442
|
+
}
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
// --- delete ---
|
|
446
|
+
if (sub === 'delete') {
|
|
447
|
+
if (wantsHelp(args)) {
|
|
448
|
+
console.log(`用法: evolclaw agent delete <aid> [--purge] [--format json]
|
|
449
|
+
|
|
450
|
+
删除 agent 的配置。
|
|
451
|
+
--purge 同时清除该 agent 的会话、消息、日志等运行时数据
|
|
452
|
+
默认 仅删除 config.json,运行时数据保留`);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
const aid = args[1];
|
|
456
|
+
if (!aid) {
|
|
457
|
+
console.error('用法: evolclaw agent delete <aid> [--purge]');
|
|
458
|
+
process.exit(1);
|
|
459
|
+
}
|
|
460
|
+
const purge = args.includes('--purge');
|
|
461
|
+
const result = await agentDelete(aid, purge);
|
|
462
|
+
if (!result.ok) {
|
|
463
|
+
if (formatJson) {
|
|
464
|
+
console.log(JSON.stringify(result));
|
|
465
|
+
}
|
|
466
|
+
else {
|
|
467
|
+
console.error(`❌ ${result.error}`);
|
|
468
|
+
}
|
|
469
|
+
process.exit(1);
|
|
470
|
+
}
|
|
471
|
+
if (formatJson) {
|
|
472
|
+
console.log(JSON.stringify(result, null, 2));
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
console.log(`✓ ${aid} deleted${purge ? ' (purged)' : ''}`);
|
|
476
|
+
}
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
// --- show ---
|
|
480
|
+
if (sub === 'show') {
|
|
481
|
+
if (wantsHelp(args)) {
|
|
482
|
+
console.log(`用法: evolclaw agent show <aid> [--format json]
|
|
483
|
+
|
|
484
|
+
查看 agent 详情:身份、配置、连接状态、会话路径等。`);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
const aid = args[1];
|
|
488
|
+
if (!aid) {
|
|
489
|
+
console.error('用法: evolclaw agent show <aid>');
|
|
490
|
+
process.exit(1);
|
|
491
|
+
}
|
|
492
|
+
const result = await agentShow(aid);
|
|
493
|
+
if (!result.ok) {
|
|
494
|
+
if (formatJson) {
|
|
495
|
+
console.log(JSON.stringify(result));
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
console.error(result.error);
|
|
499
|
+
}
|
|
500
|
+
process.exit(1);
|
|
501
|
+
}
|
|
502
|
+
if (formatJson) {
|
|
503
|
+
console.log(JSON.stringify(result, null, 2));
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
printAgentShowHuman(result);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
// --- default: `evolclaw agent <aid>` (shorthand for show) ---
|
|
510
|
+
const result = await agentShow(sub);
|
|
511
|
+
if (!result.ok) {
|
|
512
|
+
if (formatJson) {
|
|
513
|
+
console.log(JSON.stringify(result));
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
console.error(result.error);
|
|
517
|
+
}
|
|
518
|
+
process.exit(1);
|
|
519
|
+
}
|
|
520
|
+
if (formatJson) {
|
|
521
|
+
console.log(JSON.stringify(result, null, 2));
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
printAgentShowHuman(result);
|
|
525
|
+
}
|
|
526
|
+
function printAgentShowHuman(result) {
|
|
527
|
+
console.log(`${result.aid} (${result.status})\n`);
|
|
528
|
+
if (result.identity.name || result.identity.description) {
|
|
529
|
+
console.log(' Identity');
|
|
530
|
+
if (result.identity.name)
|
|
531
|
+
console.log(` Name: ${result.identity.name}`);
|
|
532
|
+
if (result.identity.description)
|
|
533
|
+
console.log(` Description: ${result.identity.description}`);
|
|
534
|
+
console.log('');
|
|
535
|
+
}
|
|
536
|
+
console.log(' Config');
|
|
537
|
+
console.log(` Baseagent: ${result.config.baseagent || '—'}`);
|
|
538
|
+
if (result.config.model)
|
|
539
|
+
console.log(` Model: ${result.config.model}`);
|
|
540
|
+
if (result.config.effort)
|
|
541
|
+
console.log(` Effort: ${result.config.effort}`);
|
|
542
|
+
if (result.config.chatmode)
|
|
543
|
+
console.log(` Chatmode: private=${result.config.chatmode.private} group=${result.config.chatmode.group}`);
|
|
544
|
+
if (result.config.owners.length)
|
|
545
|
+
console.log(` Owners: ${result.config.owners.join(', ')}`);
|
|
546
|
+
console.log(` Channels: ${result.config.channels.length > 0 ? result.config.channels.join(', ') : '—'}`);
|
|
547
|
+
console.log('');
|
|
548
|
+
if (result.connection) {
|
|
549
|
+
const c = result.connection;
|
|
550
|
+
console.log(' Connection');
|
|
551
|
+
console.log(` Status: ${c.status}`);
|
|
552
|
+
console.log(` Uptime: ${c.uptime_ms != null ? formatDurationMs(c.uptime_ms) : '—'}`);
|
|
553
|
+
console.log(` Reconnects: ${c.reconnect_count}`);
|
|
554
|
+
console.log(` Msgs recv: ${c.messages_received}`);
|
|
555
|
+
console.log(` Msgs sent: ${c.messages_sent}`);
|
|
556
|
+
console.log(` Bytes in: ${formatBytes(c.bytes_received)}`);
|
|
557
|
+
console.log(` Bytes out: ${formatBytes(c.bytes_sent)}`);
|
|
558
|
+
console.log(` Last recv: ${c.last_received_at ? formatTimeAgo(Date.now() - new Date(c.last_received_at).getTime()) : '—'}`);
|
|
559
|
+
console.log(` Last sent: ${c.last_sent_at ? formatTimeAgo(Date.now() - new Date(c.last_sent_at).getTime()) : '—'}`);
|
|
560
|
+
console.log(` Peers: ${c.unique_peer_count}`);
|
|
561
|
+
console.log('');
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
console.log(' Connection (daemon offline)');
|
|
565
|
+
console.log('');
|
|
566
|
+
}
|
|
567
|
+
console.log(' Sessions');
|
|
568
|
+
console.log(` Active: ${result.sessions.active}`);
|
|
569
|
+
console.log(` Last active: ${result.sessions.last_activity ? formatTimeAgo(Date.now() - new Date(result.sessions.last_activity).getTime()) : '—'}`);
|
|
570
|
+
console.log('');
|
|
571
|
+
console.log(' Paths');
|
|
572
|
+
console.log(` Config: ${result.paths.config}`);
|
|
573
|
+
console.log(` Agent.md: ${result.paths.agent_md}`);
|
|
574
|
+
console.log(` Project: ${result.paths.project || '—'}`);
|
|
575
|
+
console.log(` Data: ${result.paths.data}`);
|
|
576
|
+
}
|
|
577
|
+
function formatDurationMs(ms) {
|
|
578
|
+
const sec = Math.floor(ms / 1000);
|
|
579
|
+
if (sec < 60)
|
|
580
|
+
return `${sec}s`;
|
|
581
|
+
const min = Math.floor(sec / 60);
|
|
582
|
+
const s = sec % 60;
|
|
583
|
+
if (min < 60)
|
|
584
|
+
return `${min}m${String(s).padStart(2, '0')}s`;
|
|
585
|
+
const hour = Math.floor(min / 60);
|
|
586
|
+
const m = min % 60;
|
|
587
|
+
if (hour < 24)
|
|
588
|
+
return `${hour}h${String(m).padStart(2, '0')}m${String(s).padStart(2, '0')}s`;
|
|
589
|
+
const day = Math.floor(hour / 24);
|
|
590
|
+
return `${day}d${hour % 24}h${String(m).padStart(2, '0')}m`;
|
|
591
|
+
}
|
package/dist/cli/agent.js
CHANGED
|
@@ -739,15 +739,27 @@ async function agentSetEnabled(aid, enabled) {
|
|
|
739
739
|
catch (e) {
|
|
740
740
|
return { ok: false, error: `Failed to read config: ${e?.message || e}` };
|
|
741
741
|
}
|
|
742
|
+
const prevEnabled = config.enabled;
|
|
742
743
|
config.enabled = enabled;
|
|
743
744
|
saveAgent(config);
|
|
744
|
-
//
|
|
745
|
+
// Sync in-memory registry; roll back disk on failure to keep disk/memory consistent
|
|
745
746
|
let reloaded = false;
|
|
746
747
|
try {
|
|
747
748
|
const result = await ipcQuery(p.socket, { type: 'evolagent.reload', name: aid });
|
|
748
|
-
|
|
749
|
+
if (!result?.ok) {
|
|
750
|
+
// Reload rejected by daemon — roll back disk
|
|
751
|
+
config.enabled = prevEnabled;
|
|
752
|
+
saveAgent(config);
|
|
753
|
+
return { ok: false, error: result?.error || 'reload failed' };
|
|
754
|
+
}
|
|
755
|
+
reloaded = true;
|
|
756
|
+
}
|
|
757
|
+
catch (e) {
|
|
758
|
+
// IPC unreachable (daemon not running) — roll back disk
|
|
759
|
+
config.enabled = prevEnabled;
|
|
760
|
+
saveAgent(config);
|
|
761
|
+
return { ok: false, error: `IPC error: ${e?.message || e}` };
|
|
749
762
|
}
|
|
750
|
-
catch { }
|
|
751
763
|
return { ok: true, aid, enabled, reloaded };
|
|
752
764
|
}
|
|
753
765
|
// ==================== agentGet ====================
|