evolclaw 3.2.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.
Files changed (95) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +7 -4
  3. package/dist/agents/{resolve.js → baseagent.js} +34 -5
  4. package/dist/agents/claude-runner.js +120 -31
  5. package/dist/agents/codex-app-server-client.js +364 -0
  6. package/dist/agents/codex-runner.js +1152 -140
  7. package/dist/agents/gemini-runner.js +2 -2
  8. package/dist/agents/runner-types.js +58 -0
  9. package/dist/aun/aid/store.js +1 -1
  10. package/dist/aun/outbox.js +14 -2
  11. package/dist/aun/storage/download.js +1 -1
  12. package/dist/aun/storage/upload.js +13 -1
  13. package/dist/channels/aun.js +869 -358
  14. package/dist/channels/dingtalk.js +77 -140
  15. package/dist/channels/feishu.js +125 -154
  16. package/dist/channels/qqbot.js +75 -138
  17. package/dist/channels/wechat.js +75 -136
  18. package/dist/channels/wecom.js +75 -138
  19. package/dist/cli/agent-command.js +591 -0
  20. package/dist/cli/agent.js +23 -8
  21. package/dist/cli/aun-commands.js +1444 -0
  22. package/dist/cli/ctl-command.js +78 -0
  23. package/dist/cli/daemon-commands.js +2707 -0
  24. package/dist/cli/index.js +23 -4905
  25. package/dist/cli/init.js +33 -6
  26. package/dist/cli/model.js +1 -1
  27. package/dist/cli/restart-monitor.js +539 -0
  28. package/dist/cli/stats.js +558 -0
  29. package/dist/cli/version.js +87 -0
  30. package/dist/cli/watch-logs.js +33 -0
  31. package/dist/cli/watch-msg.js +5 -2
  32. package/dist/config-store.js +12 -6
  33. package/dist/core/channel-loader.js +88 -83
  34. package/dist/core/command/command-handler.js +1189 -0
  35. package/dist/core/command/menu-handler.js +1478 -0
  36. package/dist/core/command/slash-gate.js +142 -0
  37. package/dist/core/command/slash-handler.js +2090 -0
  38. package/dist/core/evolagent-registry.js +82 -0
  39. package/dist/core/evolagent.js +17 -1
  40. package/dist/core/interaction-router.js +8 -0
  41. package/dist/core/message/command-handler-agent-control.js +63 -1
  42. package/dist/core/message/im-renderer.js +91 -51
  43. package/dist/core/message/items-formatter.js +9 -1
  44. package/dist/core/message/message-bridge.js +73 -24
  45. package/dist/core/message/message-log.js +1 -0
  46. package/dist/core/message/message-processor.js +432 -94
  47. package/dist/core/message/message-queue.js +70 -2
  48. package/dist/core/message/pending-hints.js +232 -0
  49. package/dist/core/model/model-catalog.js +1 -1
  50. package/dist/core/model/model-scope.js +2 -2
  51. package/dist/core/permission.js +25 -12
  52. package/dist/core/relation/peer-identity.js +16 -1
  53. package/dist/core/session/adapters/codex-session-file-adapter.js +4 -2
  54. package/dist/core/session/session-manager.js +86 -26
  55. package/dist/core/session/session-title.js +26 -0
  56. package/dist/core/stats/billing.js +151 -0
  57. package/dist/core/stats/budget.js +93 -0
  58. package/dist/core/stats/db.js +334 -0
  59. package/dist/core/stats/eck-vars.js +84 -0
  60. package/dist/core/stats/index.js +10 -0
  61. package/dist/core/stats/normalizer.js +78 -0
  62. package/dist/core/stats/query.js +760 -0
  63. package/dist/core/stats/writer.js +115 -0
  64. package/dist/core/trigger/manager.js +34 -0
  65. package/dist/core/trigger/parser.js +9 -3
  66. package/dist/core/trigger/scheduler.js +20 -17
  67. package/dist/data/error-dict.json +7 -0
  68. package/dist/{agents → eck}/manifest-engine.js +20 -1
  69. package/dist/{agents → eck}/message-renderer.js +24 -1
  70. package/dist/index.js +174 -9
  71. package/dist/ipc.js +116 -1
  72. package/dist/utils/cross-platform.js +58 -5
  73. package/dist/utils/ecweb-launch.js +49 -0
  74. package/dist/utils/ecweb-pair.js +20 -0
  75. package/dist/utils/error-utils.js +18 -5
  76. package/dist/utils/npm-ops.js +38 -8
  77. package/dist/utils/stats.js +77 -6
  78. package/kits/docs/evolclaw/INDEX.md +3 -1
  79. package/kits/docs/evolclaw/fs-architecture.md +1215 -0
  80. package/kits/docs/evolclaw/fs.md +131 -0
  81. package/kits/docs/evolclaw/group-fs.md +209 -0
  82. package/kits/docs/evolclaw/stats.md +70 -0
  83. package/kits/docs/venues/aun-group.md +29 -6
  84. package/kits/docs/venues/group.md +5 -4
  85. package/kits/eck_message_manifest.json +30 -3
  86. package/kits/rules/05-venue.md +1 -1
  87. package/kits/templates/message-fragments/inject-default.md +2 -0
  88. package/package.json +5 -6
  89. package/dist/agents/baseagent-normalize.js +0 -19
  90. package/dist/core/command-handler.js +0 -3876
  91. package/dist/core/relation/peer-key.js +0 -16
  92. package/dist/evolclaw-config.js +0 -11
  93. package/dist/utils/channel-helpers.js +0 -46
  94. /package/dist/core/{cache/file-cache.js → daemon-file-cache.js} +0 -0
  95. /package/dist/{agents → eck}/kit-renderer.js +0 -0
@@ -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
@@ -7,12 +7,12 @@ import { ipcQuery } from '../ipc.js';
7
7
  import { CONFIG_SCHEMA_VERSION } from '../types.js';
8
8
  import { isValidChannelName } from '../core/channel-loader.js';
9
9
  import { commandExists } from '../utils/cross-platform.js';
10
- import { isCodexSdkAvailable } from '../agents/codex-runner.js';
10
+ import { getCodexAppServerAvailability, isCodexAppServerAvailable } from '../agents/codex-runner.js';
11
11
  // ==================== Helpers ====================
12
12
  const BASEAGENT_CANDIDATES = ['claude', 'codex', 'gemini'];
13
13
  function isBaseagentAvailable(baseagent) {
14
14
  if (baseagent === 'codex')
15
- return isCodexSdkAvailable();
15
+ return isCodexAppServerAvailable();
16
16
  return commandExists(baseagent);
17
17
  }
18
18
  function detectAvailableBaseagents() {
@@ -313,7 +313,7 @@ export async function agentCreateInteractive(opts = {}) {
313
313
  // Baseagent
314
314
  const available = detectAvailableBaseagents();
315
315
  if (available.length === 0) {
316
- return { ok: false, error: `No usable baseagent detected. Install claude/gemini CLI or optional dependency @openai/codex-sdk.` };
316
+ return { ok: false, error: `No usable baseagent detected. Install claude/gemini CLI or codex CLI with app-server.` };
317
317
  }
318
318
  const defaultBa = pickDefaultBaseagent(available);
319
319
  let baseagent;
@@ -467,7 +467,7 @@ export async function agentCreateNonInteractive(opts) {
467
467
  // Baseagent
468
468
  const available = detectAvailableBaseagents();
469
469
  if (available.length === 0) {
470
- return failValidating(`No usable baseagent detected. Install claude/gemini CLI or optional dependency @openai/codex-sdk.`);
470
+ return failValidating(`No usable baseagent detected. Install claude/gemini CLI or codex CLI with app-server.`);
471
471
  }
472
472
  let baseagent;
473
473
  if (opts.baseagent) {
@@ -475,7 +475,10 @@ export async function agentCreateNonInteractive(opts) {
475
475
  return failValidating(`Invalid baseagent: ${opts.baseagent} (options: ${BASEAGENT_CANDIDATES.join('/')})`);
476
476
  }
477
477
  if (!available.includes(opts.baseagent)) {
478
- return failValidating(`${opts.baseagent} is not available in the current environment (available: ${available.join('/')})`);
478
+ const reason = opts.baseagent === 'codex'
479
+ ? getCodexAppServerAvailability().reason
480
+ : undefined;
481
+ return failValidating(reason || `${opts.baseagent} is not available in the current environment (available: ${available.join('/')})`);
479
482
  }
480
483
  baseagent = opts.baseagent;
481
484
  }
@@ -736,15 +739,27 @@ async function agentSetEnabled(aid, enabled) {
736
739
  catch (e) {
737
740
  return { ok: false, error: `Failed to read config: ${e?.message || e}` };
738
741
  }
742
+ const prevEnabled = config.enabled;
739
743
  config.enabled = enabled;
740
744
  saveAgent(config);
741
- // Try hot-reload
745
+ // Sync in-memory registry; roll back disk on failure to keep disk/memory consistent
742
746
  let reloaded = false;
743
747
  try {
744
748
  const result = await ipcQuery(p.socket, { type: 'evolagent.reload', name: aid });
745
- reloaded = !!result?.ok;
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}` };
746
762
  }
747
- catch { }
748
763
  return { ok: true, aid, enabled, reloaded };
749
764
  }
750
765
  // ==================== agentGet ====================