evolclaw 3.1.11 → 3.3.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 (89) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +27 -2
  3. package/dist/agents/{resolve.js → baseagent.js} +34 -5
  4. package/dist/agents/claude-runner.js +120 -27
  5. package/dist/agents/codex-app-server-client.js +364 -0
  6. package/dist/agents/codex-runner.js +1069 -141
  7. package/dist/agents/gemini-runner.js +2 -2
  8. package/dist/agents/runner-types.js +28 -0
  9. package/dist/aun/aid/control-aid.js +67 -0
  10. package/dist/aun/aid/identity.js +20 -7
  11. package/dist/aun/aid/store.js +2 -2
  12. package/dist/aun/storage/download.js +1 -1
  13. package/dist/aun/storage/upload.js +13 -1
  14. package/dist/channels/aun.js +538 -325
  15. package/dist/channels/dingtalk.js +77 -140
  16. package/dist/channels/feishu.js +98 -151
  17. package/dist/channels/qqbot.js +75 -138
  18. package/dist/channels/wechat.js +75 -136
  19. package/dist/channels/wecom.js +75 -138
  20. package/dist/cli/agent.js +44 -13
  21. package/dist/cli/index.js +207 -46
  22. package/dist/cli/init-channel.js +38 -148
  23. package/dist/cli/init.js +192 -85
  24. package/dist/cli/model.js +1 -1
  25. package/dist/cli/stats.js +558 -0
  26. package/dist/cli/version.js +87 -0
  27. package/dist/cli/watch-msg.js +5 -2
  28. package/dist/config-store.js +48 -11
  29. package/dist/core/channel-loader.js +84 -82
  30. package/dist/core/command-handler.js +754 -172
  31. package/dist/core/daemon-file-cache.js +216 -0
  32. package/dist/core/evolagent-registry.js +4 -0
  33. package/dist/core/evolagent.js +28 -23
  34. package/dist/core/interaction-router.js +8 -0
  35. package/dist/core/message/command-handler-agent-control.js +215 -0
  36. package/dist/core/message/create-status.js +67 -0
  37. package/dist/core/message/im-renderer.js +35 -13
  38. package/dist/core/message/items-formatter.js +9 -1
  39. package/dist/core/message/message-bridge.js +52 -22
  40. package/dist/core/message/message-log.js +1 -0
  41. package/dist/core/message/message-processor.js +336 -68
  42. package/dist/core/message/message-queue.js +15 -8
  43. package/dist/core/message/pending-hints.js +232 -0
  44. package/dist/core/message/response-depth.js +56 -0
  45. package/dist/core/model/model-catalog.js +1 -1
  46. package/dist/core/model/model-scope.js +40 -7
  47. package/dist/core/permission.js +9 -12
  48. package/dist/core/relation/peer-identity.js +16 -1
  49. package/dist/core/session/adapters/claude-session-file-adapter.js +48 -5
  50. package/dist/core/session/adapters/codex-session-file-adapter.js +4 -2
  51. package/dist/core/session/session-manager.js +27 -13
  52. package/dist/core/session/session-title.js +26 -0
  53. package/dist/core/stats/billing.js +151 -0
  54. package/dist/core/stats/budget.js +93 -0
  55. package/dist/core/stats/db.js +314 -0
  56. package/dist/core/stats/eck-vars.js +84 -0
  57. package/dist/core/stats/index.js +10 -0
  58. package/dist/core/stats/normalizer.js +78 -0
  59. package/dist/core/stats/query.js +760 -0
  60. package/dist/core/stats/writer.js +115 -0
  61. package/dist/core/trigger/manager.js +34 -0
  62. package/dist/core/trigger/parser.js +9 -3
  63. package/dist/core/trigger/scheduler.js +20 -17
  64. package/dist/{agents → eck}/kit-renderer.js +5 -1
  65. package/dist/{agents → eck}/manifest-engine.js +127 -35
  66. package/dist/{agents → eck}/message-renderer.js +26 -1
  67. package/dist/index.js +185 -8
  68. package/dist/ipc.js +22 -0
  69. package/dist/paths.js +7 -3
  70. package/dist/utils/cross-platform.js +23 -5
  71. package/dist/utils/ecweb-pair.js +20 -0
  72. package/dist/utils/stats.js +14 -0
  73. package/kits/docs/evolclaw/INDEX.md +3 -1
  74. package/kits/docs/evolclaw/fs-architecture.md +1215 -0
  75. package/kits/docs/evolclaw/fs.md +131 -0
  76. package/kits/docs/evolclaw/group-fs.md +209 -0
  77. package/kits/docs/evolclaw/stats.md +70 -0
  78. package/kits/docs/venues/aun-group.md +29 -6
  79. package/kits/docs/venues/group.md +5 -4
  80. package/kits/eck_manifest.json +12 -0
  81. package/kits/eck_message_manifest.json +30 -3
  82. package/kits/rules/05-venue.md +1 -1
  83. package/kits/templates/message-fragments/inject-default.md +2 -0
  84. package/kits/templates/message-fragments/item.md +1 -1
  85. package/kits/templates/system-fragments/response-depth.md +16 -0
  86. package/package.json +4 -4
  87. package/dist/agents/baseagent-normalize.js +0 -19
  88. package/dist/core/relation/peer-key.js +0 -16
  89. package/dist/utils/channel-helpers.js +0 -46
package/dist/cli/index.js CHANGED
@@ -5,18 +5,21 @@ import os from 'os';
5
5
  import { spawn, execFileSync, execFile } from 'child_process';
6
6
  import { promisify } from 'util';
7
7
  import { resolveRoot, resolvePaths, ensureDataDirs, getPackageRoot, agentMdPath } from '../paths.js';
8
- import { loadDefaults, loadAllAgents, mergeForAgent } from '../config-store.js';
9
- import { resolveAnthropicConfig } from '../agents/resolve.js';
8
+ import { loadDefaults, loadAllAgents, mergeForAgent, loadEvolclawConfig, saveEvolclawConfig } from '../config-store.js';
9
+ import { resolveAnthropicConfig } from '../agents/baseagent.js';
10
10
  import { migrateProject } from '../config-store.js';
11
- import { cmdInit } from './init.js';
11
+ import readline from 'readline';
12
+ import { cmdInit, needsControlAidInit, initTail } from './init.js';
12
13
  import { ipcQuery } from '../ipc.js';
13
- import { cmdInitWechat, cmdInitFeishu, cmdInitDingtalk, cmdInitQQBot, cmdInitWecom } from './init-channel.js';
14
+ import { cmdInitWechat, cmdInitFeishu, cmdInitDingtalk, cmdInitQQBot, cmdInitWecom, cmdInitAun } from './init-channel.js';
14
15
  import { isHelpFlag, wantsHelp, getArgValue } from './help.js';
15
16
  import * as platform from '../utils/cross-platform.js';
16
17
  import { EventBus } from '../core/event-bus.js';
17
18
  import { tryUpgrade, tryUpgradeAunSdk } from '../utils/npm-ops.js';
19
+ import { fetchEcwebPairCode } from '../utils/ecweb-pair.js';
18
20
  import { resolveAunCoreSdkPkg, AUN_CORE_SDK_PKG } from '../aun/aid/client.js';
19
21
  import { scanInstances, cleanupInstances, writeRestartMonitor, removeRestartMonitor, isRestartMonitorWinner, findOrphanProcesses, killOrphans } from '../utils/instance-registry.js';
22
+ import { displaySessionTitle } from '../core/session/session-title.js';
20
23
  // Suppress Node.js ExperimentalWarning (e.g. SQLite) from cluttering CLI output
21
24
  process.removeAllListeners('warning');
22
25
  process.on('warning', (w) => { if (w.name === 'ExperimentalWarning')
@@ -299,6 +302,40 @@ async function cmdStart() {
299
302
  await cmdInit();
300
303
  return;
301
304
  }
305
+ // 控制 AID 门禁:缺 aid 且交互式 → 只补全控制 AID + owners(不重走 baseagent 向导)。
306
+ // 非 TTY(restart-monitor/systemd/管道)不补全(无法交互),只提示后继续启动,daemon 侧 warn 兜底。
307
+ const evolclawCfgStart = loadEvolclawConfig();
308
+ if (needsControlAidInit(evolclawCfgStart.aid, !!process.stdin.isTTY)) {
309
+ console.log('⚡ 控制 AID 未配置,自动补全...\n');
310
+ const { suppressSdkLogs } = await import('../aun/aid/index.js');
311
+ suppressSdkLogs();
312
+ await initTail();
313
+ return;
314
+ }
315
+ if (!evolclawCfgStart.aid) {
316
+ console.log('⚠ 控制 AID 未配置(非交互式启动,跳过补全)。如需进程身份/远程管理,请运行 evolclaw init');
317
+ }
318
+ else if (process.stdin.isTTY) {
319
+ // 证书缺失时在 CLI 侧提示,daemon 是后台进程无终端不做交互
320
+ const certKey = path.join(resolvePaths().root, 'AIDs', evolclawCfgStart.aid, 'private', 'key.json');
321
+ if (!fs.existsSync(certKey)) {
322
+ console.log(`⚠ 控制 AID 证书缺失:${evolclawCfgStart.aid}`);
323
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
324
+ const ans = await new Promise(res => rl.question(' [1] 继续启动 [2] 重新生成 AID [3] 退出 [1/2/3]: ', res));
325
+ rl.close();
326
+ if (ans.trim() === '3') {
327
+ process.exit(0);
328
+ }
329
+ if (ans.trim() === '2') {
330
+ const { suppressSdkLogs } = await import('../aun/aid/index.js');
331
+ suppressSdkLogs();
332
+ const { generateControlAid } = await import('../aun/aid/control-aid.js');
333
+ const result = await generateControlAid();
334
+ saveEvolclawConfig({ ...loadEvolclawConfig(), aid: result.aid });
335
+ console.log(`✓ 新控制 AID: ${result.aid}`);
336
+ }
337
+ }
338
+ }
302
339
  // 检查至少有一个 self-agent
303
340
  const { agents, skipped } = loadAllAgents();
304
341
  if (agents.length === 0) {
@@ -422,6 +459,8 @@ async function cmdStart() {
422
459
  countLines(getPackageRoot(), p.logs);
423
460
  }
424
461
  console.log(`⏱ done in ${((Date.now() - cmdStartedAt) / 1000).toFixed(1)}s`);
462
+ // ECWeb 自动后台启动
463
+ startEcwebIfEnabled(p);
425
464
  return;
426
465
  }
427
466
  // 超时
@@ -900,7 +939,7 @@ async function cmdStatus() {
900
939
  const projectName = path.basename(s.projectPath);
901
940
  const sessionType = s.threadId ? '话题会话' : '主会话';
902
941
  const chatType = s.chatType === 'group' ? '群聊' : '单聊';
903
- const sessionName = s.name || '默认会话';
942
+ const sessionName = displaySessionTitle(s.name);
904
943
  const timeAgo = formatTimeAgo(Date.now() - s.updatedAt);
905
944
  const dot = s.isActive ? '•' : '○';
906
945
  const agentSidLabel = s.agentSessionId ? ` [${s.agentSessionId}]` : '';
@@ -968,6 +1007,14 @@ async function cmdStatus() {
968
1007
  }
969
1008
  }
970
1009
  catch { /* ignore */ }
1010
+ // 控制 AID(daemon 进程身份)状态
1011
+ if (status.controlAid) {
1012
+ const state = status.controlAid.connected ? 'connected' : 'disconnected';
1013
+ console.log(`control: ${status.controlAid.aid} [${state}]`);
1014
+ }
1015
+ else {
1016
+ console.log('control: not configured');
1017
+ }
971
1018
  if (status.stats) {
972
1019
  console.log('');
973
1020
  console.log('📊 Last hour:');
@@ -2101,54 +2148,129 @@ async function cmdWatchAid() {
2101
2148
  }
2102
2149
  platform.onShutdown(cleanup);
2103
2150
  }
2151
+ /** 扫描 instance/ 目录,返回存活的 ecweb 实例(ecweb-<pid>.json)。 */
2152
+ function findAliveEcweb(p) {
2153
+ if (!fs.existsSync(p.instanceDir))
2154
+ return null;
2155
+ for (const file of fs.readdirSync(p.instanceDir)) {
2156
+ if (!file.startsWith('ecweb-') || !file.endsWith('.json'))
2157
+ continue;
2158
+ try {
2159
+ const rec = JSON.parse(fs.readFileSync(path.join(p.instanceDir, file), 'utf-8'));
2160
+ if (rec.pid && platform.isProcessRunning(rec.pid))
2161
+ return { pid: rec.pid, port: rec.port ?? 42705 };
2162
+ fs.unlinkSync(path.join(p.instanceDir, file));
2163
+ }
2164
+ catch { }
2165
+ }
2166
+ return null;
2167
+ }
2168
+ /** 若 ecweb 在运行则杀掉并清理 pid 文件,返回是否成功 kill。 */
2169
+ function stopEcwebIfRunning(p) {
2170
+ const alive = findAliveEcweb(p);
2171
+ if (!alive)
2172
+ return false;
2173
+ try {
2174
+ platform.killProcess(alive.pid);
2175
+ }
2176
+ catch { }
2177
+ // 清理 pid 文件
2178
+ try {
2179
+ for (const file of fs.readdirSync(p.instanceDir)) {
2180
+ if (file.startsWith('ecweb-') && file.endsWith('.json')) {
2181
+ fs.unlinkSync(path.join(p.instanceDir, file));
2182
+ }
2183
+ }
2184
+ }
2185
+ catch { }
2186
+ return true;
2187
+ }
2188
+ /** 后台 detached 启动 ecweb;若已运行则先停再启(确保加载最新代码)。 */
2189
+ function startEcwebIfEnabled(p) {
2190
+ const cfg = loadEvolclawConfig();
2191
+ if (!cfg.ecweb?.enabled)
2192
+ return;
2193
+ stopEcwebIfRunning(p); // 先停旧进程(有则停),保证加载最新代码
2194
+ const exe = platform.resolveCommandPath('evolclaw-web');
2195
+ if (!exe)
2196
+ return; // 未安装,静默跳过
2197
+ const port = cfg.ecweb.port ?? 42705;
2198
+ const isBatch = /\.(cmd|bat)$/i.test(exe);
2199
+ const args = ['--home', p.root, '--port', String(port)];
2200
+ const child = isBatch
2201
+ ? spawn(`"${exe}"`, args.map(a => `"${a}"`), { detached: true, stdio: 'ignore', shell: true, windowsHide: true })
2202
+ : spawn(exe, args, { detached: true, stdio: 'ignore', windowsHide: true });
2203
+ child.unref();
2204
+ const pid = child.pid;
2205
+ if (!pid)
2206
+ return;
2207
+ fs.mkdirSync(p.instanceDir, { recursive: true });
2208
+ fs.writeFileSync(path.join(p.instanceDir, `ecweb-${pid}.json`), JSON.stringify({ pid, port, startedAt: Date.now() }, null, 2));
2209
+ console.log(`🔭 ECWeb 已在后台启动 (PID: ${pid}) http://localhost:${port}`);
2210
+ console.log(` 运行 ec watch web 查看配对码`);
2211
+ }
2212
+ /** 显示 ecweb 访问信息 + 配对码(启动后 ecweb 需要一点时间起 HTTP,故重试几次)。 */
2213
+ async function printEcwebAccess(port) {
2214
+ console.log(`🔭 ECWeb http://localhost:${port}`);
2215
+ let pair = null;
2216
+ for (let i = 0; i < 10 && !pair; i++) {
2217
+ pair = await fetchEcwebPairCode(port);
2218
+ if (!pair)
2219
+ await sleep(300);
2220
+ }
2221
+ if (pair) {
2222
+ const mins = Math.max(0, Math.round((pair.expiresAt - Date.now()) / 60000));
2223
+ console.log(` 配对码: ${pair.code} (约 ${mins} 分钟内有效,配对后 token 缓存 24h)`);
2224
+ }
2225
+ else {
2226
+ console.log(' 配对码: 暂不可用(稍后重试 ec watch web,或查看 logs/watch-web.log)');
2227
+ }
2228
+ }
2104
2229
  async function cmdWatchWeb() {
2105
- // evolclaw-web 是独立插件包(可执行命令),按需安装。
2106
- // 复用 npm-ops.npmInstallGlobal(含 EACCES→sudo 回退、Windows npm.cmd、超时)。
2107
- const { execFileSync } = await import('child_process');
2108
- const home = resolvePaths().root;
2230
+ const p = resolvePaths();
2231
+ // 1. 检查安装
2109
2232
  if (!platform.commandExists('evolclaw-web')) {
2110
- process.stdout.write('📦 evolclaw-web 未安装,正在从 npm 安装...\n');
2233
+ process.stdout.write('📦 evolclaw-web 未安装。');
2234
+ if (!process.stdin.isTTY) {
2235
+ process.stdout.write(' 请手动安装: npm install -g evolclaw-web\n');
2236
+ process.exit(1);
2237
+ }
2238
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
2239
+ const ans = await new Promise(res => rl.question(' 立即安装?[Y/n] ', res));
2240
+ rl.close();
2241
+ if (ans.trim().toLowerCase() === 'n') {
2242
+ process.exit(0);
2243
+ }
2244
+ process.stdout.write('\n');
2111
2245
  const { npmInstallGlobal } = await import('../utils/npm-ops.js');
2112
2246
  try {
2113
2247
  await npmInstallGlobal('evolclaw-web');
2114
2248
  }
2115
2249
  catch (e) {
2116
- process.stderr.write(`❌ 安装 evolclaw-web 失败: ${e?.stderr || e?.message || e}\n 可手动安装: npm install -g evolclaw-web\n`);
2250
+ process.stderr.write(`❌ 安装失败: ${e?.stderr || e?.message || e}\n`);
2117
2251
  process.exit(1);
2118
2252
  }
2119
2253
  }
2120
- // 解析可执行文件的真实绝对路径:
2121
- // - Windows bin 是 evolclaw-web.cmd,execFileSync 不会自动补后缀
2122
- // - 刚安装的命令可能不在当前进程已缓存的 PATH 里,用 where/which 重新探测
2123
- const exe = platform.resolveCommandPath('evolclaw-web');
2124
- if (!exe) {
2125
- process.stderr.write('❌ 已安装 evolclaw-web 但无法定位可执行文件。\n 请重新打开终端后再次运行,或手动执行: evolclaw-web --home ' + home + '\n');
2126
- process.exit(1);
2127
- }
2128
- // Node 18.20+/20+/22 起,execFile 拒绝直接 spawn .cmd/.bat(CVE-2024-27980),必须 shell:true。
2129
- // shell 模式下含空格的路径/参数需加引号。
2130
- // evolclaw-web 是前台长驻服务:用户 Ctrl-C、被新实例的单实例保护 SIGKILL、或正常退出,
2131
- // execFileSync 都会抛错(signal 终止时 status=null)。这些都是正常生命周期,
2132
- // 不应让父进程 evolclaw 带堆栈崩溃。只有真正的非信号失败才提示。
2133
- const isBatch = /\.(cmd|bat)$/i.test(exe);
2134
- try {
2135
- if (isBatch) {
2136
- const q = (s) => `"${s}"`;
2137
- execFileSync(q(exe), ['--home', q(home)], { stdio: 'inherit', shell: true });
2138
- }
2139
- else {
2140
- execFileSync(exe, ['--home', home], { stdio: 'inherit' });
2141
- }
2254
+ // 2. 检查是否已运行
2255
+ const alive = findAliveEcweb(p);
2256
+ if (alive) {
2257
+ await printEcwebAccess(alive.port);
2258
+ return;
2142
2259
  }
2143
- catch (e) {
2144
- // 信号终止(SIGINT/SIGTERM/SIGKILL)= 用户主动退出或被新实例顶替,静默返回
2145
- if (e?.signal)
2146
- return;
2147
- // 退出码非 0 但非信号:可能是启动失败,提示但不崩溃
2148
- if (typeof e?.status === 'number' && e.status !== 0) {
2149
- process.stderr.write(`⚠ evolclaw-web 退出(code ${e.status})\n`);
2150
- }
2260
+ // 3. 启动(后台)并同步配置
2261
+ const cfg = loadEvolclawConfig();
2262
+ const port = cfg.ecweb?.port ?? 42705;
2263
+ if (cfg.ecweb?.enabled === undefined) {
2264
+ // 首次手动启动时自动写入 enabled:true
2265
+ saveEvolclawConfig({ ...cfg, ecweb: { enabled: true, port } });
2266
+ }
2267
+ startEcwebIfEnabled(p);
2268
+ const started = findAliveEcweb(p);
2269
+ if (!started) {
2270
+ process.stderr.write('❌ 启动失败,请检查 evolclaw-web 是否正确安装\n');
2271
+ process.exit(1);
2151
2272
  }
2273
+ await printEcwebAccess(started.port);
2152
2274
  }
2153
2275
  async function cmdRestartMonitor() {
2154
2276
  const p = resolvePaths();
@@ -2805,13 +2927,23 @@ async function cmdCtl(args) {
2805
2927
  Agent:
2806
2928
  agent <subcommand> EvolAgent 管理(list/show/new/enable/disable/reload/delete)
2807
2929
 
2930
+ 触发器:
2931
+ trigger 查看活跃触发器
2932
+ trigger list 查看所有触发器(含历史)
2933
+ trigger set --delay <时长> --prompt <内容> 延迟触发(如 15m、2h)
2934
+ trigger set --at <ISO时间> --prompt <内容> 定时触发(如 2026-06-10T09:00)
2935
+ trigger set --cron '<表达式>' --prompt <内容> 周期触发(如 '*/15 * * * *')
2936
+ trigger cancel <名称> 取消触发器
2937
+ trigger update <名称> ... 修改触发器参数
2938
+
2808
2939
  运维:
2809
2940
  restart [channel] 重启服务或重连指定渠道
2810
2941
 
2811
2942
  示例:
2812
2943
  evolclaw ctl model sonnet
2813
2944
  evolclaw ctl effort high
2814
- evolclaw ctl compact`);
2945
+ evolclaw ctl compact
2946
+ evolclaw ctl "trigger set --cron '*/15 * * * *' --prompt '现在时间?'"`);
2815
2947
  process.exit(1);
2816
2948
  }
2817
2949
  // help 不需要连接服务,直接复用无参数时的帮助输出
@@ -4053,11 +4185,16 @@ Commands:
4053
4185
  process.exit(1);
4054
4186
  }
4055
4187
  if (formatJson) {
4056
- console.log(JSON.stringify({ ok: true, objectKey: remotePath, isPublic, ref: `${aid}/${remotePath}` }));
4188
+ console.log(JSON.stringify({ ok: true, objectKey: remotePath, isPublic, ref: `${aid}/${remotePath}`, publicUrl: result.publicUrl ?? null }));
4057
4189
  }
4058
4190
  else {
4059
4191
  console.log(`✓ 已上传: ${remotePath}${isPublic ? ' (公开)' : ''}`);
4060
- console.log(` 引用: ${aid}/${remotePath}`);
4192
+ if (result.publicUrl) {
4193
+ console.log(` 🔗 访问: ${result.publicUrl}`);
4194
+ }
4195
+ else {
4196
+ console.log(` 引用: ${aid}/${remotePath}`);
4197
+ }
4061
4198
  console.log(` 下载: evolclaw storage download ${aid} ${aid}/${remotePath}`);
4062
4199
  }
4063
4200
  return;
@@ -4870,12 +5007,18 @@ export async function main(args) {
4870
5007
  --force 已存在 defaults.json 时覆盖
4871
5008
 
4872
5009
  配置渠道(先 evolclaw agent new 创建 agent):
5010
+ evolclaw init aun AUN 渠道配置(AID 创建/绑定)
4873
5011
  evolclaw init feishu 飞书扫码登录
4874
5012
  evolclaw init wechat 微信扫码登录
4875
5013
  evolclaw init dingtalk 钉钉扫码登录
4876
5014
  evolclaw init qqbot QQ 机器人扫码绑定
4877
5015
  evolclaw init wecom 企业微信 AI Bot 配置(手动输入)`);
4878
5016
  }
5017
+ else if (args[1] === 'aun') {
5018
+ const { suppressSdkLogs } = await import('../aun/aid/index.js');
5019
+ suppressSdkLogs();
5020
+ await cmdInitAun();
5021
+ }
4879
5022
  else if (args[1] === 'wechat') {
4880
5023
  const { suppressSdkLogs } = await import('../aun/aid/index.js');
4881
5024
  suppressSdkLogs();
@@ -4900,7 +5043,7 @@ export async function main(args) {
4900
5043
  await cmdInitWecom();
4901
5044
  }
4902
5045
  else if (args[1] && !args[1].startsWith('-')) {
4903
- const supported = ['feishu', 'wechat', 'dingtalk', 'qqbot', 'wecom'];
5046
+ const supported = ['feishu', 'wechat', 'aun', 'dingtalk', 'qqbot', 'wecom'];
4904
5047
  console.error(`❌ 不支持的渠道: ${args[1]}`);
4905
5048
  console.error(` 支持的渠道: ${supported.join(', ')}`);
4906
5049
  process.exit(1);
@@ -4931,7 +5074,12 @@ export async function main(args) {
4931
5074
  case 'logs':
4932
5075
  cmdLogs(args.slice(1));
4933
5076
  break;
4934
- case 'watch':
5077
+ case 'watch': {
5078
+ // watch 子命令(aid/msg)会调 AUN SDK(aidLookup 刷名片、对端探测等),
5079
+ // 与 aid/msg/group 等命令一致:进 case 先关掉 SDK 的 [aun_core] 日志,
5080
+ // 否则 SDK debug 日志会直喷终端、糊住 watch 的 TUI 面板。
5081
+ const { suppressSdkLogs } = await import('../aun/aid/index.js');
5082
+ suppressSdkLogs();
4935
5083
  if (args[1] === 'aid') {
4936
5084
  await cmdWatchAid();
4937
5085
  }
@@ -4971,6 +5119,7 @@ export async function main(args) {
4971
5119
  cmdWatch();
4972
5120
  }
4973
5121
  break;
5122
+ }
4974
5123
  case 'restart-monitor':
4975
5124
  await cmdRestartMonitor();
4976
5125
  break;
@@ -5032,6 +5181,18 @@ export async function main(args) {
5032
5181
  await cmdModel(args.slice(1));
5033
5182
  break;
5034
5183
  }
5184
+ case 'stats': {
5185
+ const { handleStats } = await import('./stats.js');
5186
+ await handleStats(args.slice(1));
5187
+ break;
5188
+ }
5189
+ case 'version':
5190
+ case '-v':
5191
+ case '--version': {
5192
+ const { handleVersion } = await import('./version.js');
5193
+ handleVersion(args.slice(1));
5194
+ break;
5195
+ }
5035
5196
  case 'bench': {
5036
5197
  const { suppressSdkLogs } = await import('../aun/aid/index.js');
5037
5198
  suppressSdkLogs();
@@ -5,16 +5,12 @@
5
5
  * - cmdInit<Channel>() — standalone `evolclaw init <channel>` entry
6
6
  * - run<Channel>QrFlow() or setupAun*() — reusable primitives for the main init wizard
7
7
  */
8
- import fs from 'fs';
9
8
  import readline from 'readline';
10
- import path from 'path';
11
9
  import crypto from 'crypto';
12
- import { aidLocalDir, aunPath as defaultAunPath } from '../paths.js';
13
10
  import { selectInstance } from './init.js';
14
- import { npmInstallGlobal } from '../utils/npm-ops.js';
15
- import { loadAllAgents, loadAgent } from '../config-store.js';
11
+ import { loadAllAgents, loadAgent, saveAgent } from '../config-store.js';
16
12
  import { agentChannelUpsert } from './agent.js';
17
- import { AUN_CORE_SDK_PKG, MIN_AUN_CORE_SDK, resolveAunCoreSdkPkg, isAunSdkVersionOk, isValidAid, aidCreate, agentmdPut, buildInitialAgentMd, } from '../aun/aid/index.js';
13
+ import { isValidAid } from '../aun/aid/index.js';
18
14
  function ask(rl, question) {
19
15
  return new Promise(resolve => rl.question(question, resolve));
20
16
  }
@@ -372,156 +368,50 @@ export async function cmdInitWechat() {
372
368
  await commitChannel(aid, channel, choice.action);
373
369
  }
374
370
  // ==================== AUN ====================
375
- //
376
- // AUN 原子操作(aidCreate, agentmdPut, downloadCaRoot, isValidAid, ...)
377
- // 已迁移至 src/channels/aun-ops.ts。本节仅保留交互式 UI 编排。
378
- export async function checkAunEnvironment(rl) {
379
- console.log('\n🔍 AUN 环境检查...\n');
380
- const minVer = MIN_AUN_CORE_SDK.join('.');
381
- const installed = resolveAunCoreSdkPkg();
382
- if (!installed) {
383
- console.log(` ✗ ${AUN_CORE_SDK_PKG} 未安装`);
384
- const answer = (await ask(rl, ` → 是否安装 ${AUN_CORE_SDK_PKG}@latest?[Y/n] `)).trim().toLowerCase();
385
- if (answer === 'n' || answer === 'no') {
386
- console.log(' 已取消');
387
- return false;
388
- }
389
- console.log(` 正在安装 ${AUN_CORE_SDK_PKG}...`);
390
- try {
391
- await npmInstallGlobal(`${AUN_CORE_SDK_PKG}@latest`);
392
- console.log(` ✓ ${AUN_CORE_SDK_PKG} 安装完成`);
393
- }
394
- catch (e) {
395
- console.log(` ✗ 安装失败: ${e.message?.slice(0, 200) || e}`);
396
- return false;
397
- }
398
- console.log('');
399
- return true;
400
- }
401
- if (isAunSdkVersionOk(installed.version)) {
402
- console.log(` ✓ ${AUN_CORE_SDK_PKG} v${installed.version}`);
403
- console.log('');
404
- return true;
405
- }
406
- console.log(` ✗ ${AUN_CORE_SDK_PKG} v${installed.version} — 需要 >= ${minVer}`);
407
- const answer = (await ask(rl, ` → 是否升级 ${AUN_CORE_SDK_PKG}?[Y/n] `)).trim().toLowerCase();
408
- if (answer === 'n' || answer === 'no') {
409
- console.log(' 已取消');
410
- return false;
411
- }
412
- console.log(` 正在升级 ${AUN_CORE_SDK_PKG}...`);
371
+ export async function cmdInitAun() {
372
+ // AUN channel agent.aid 隐式派生,AID 密钥在 `agent new` 时已创建就绪——本命令不碰 aid。
373
+ // 与其他渠道一致,职责是配置 owner;AUN 的 owner 存于 agent 顶层 owners[](见 evolagent.ts)。
374
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
413
375
  try {
414
- await npmInstallGlobal(`${AUN_CORE_SDK_PKG}@latest`);
415
- console.log(` ✓ ${AUN_CORE_SDK_PKG} 升级完成`);
416
- }
417
- catch (e) {
418
- console.log(` ✗ 升级失败: ${e.message?.slice(0, 200) || e}`);
419
- return false;
420
- }
421
- console.log('');
422
- return true;
423
- }
424
- // isValidAid, createAidSilent → 已迁移至 src/channels/aun-ops.ts
425
- // appendAunInstance → 已迁移至 src/channels/aun-ops.ts
426
- export async function setupAunAid(rl, _config) {
427
- let aid = '';
428
- // Outer loop: allows retrying with a different AID
429
- while (true) {
430
- // Ask AID with format validation
431
- aid = '';
432
- while (!aid) {
433
- aid = (await ask(rl, ' AUN Agent ID (例: mybot.agentid.pub): ')).trim();
434
- if (!aid) {
435
- console.log(' ⚠ 不能为空');
436
- continue;
437
- }
438
- if (!isValidAid(aid)) {
439
- console.log(' ⚠ 无效 AID 格式(需要合法域名,至少三级,如 alice.agentid.pub)');
440
- aid = '';
441
- }
442
- }
443
- // Check if AID exists locally
444
- const aunPath = defaultAunPath();
445
- const aidDir = path.join(aunPath, 'AIDs', aid);
446
- if (fs.existsSync(aidDir) && fs.existsSync(path.join(aidDir, 'private'))) {
447
- console.log(` ✓ AID ${aid} 已存在`);
448
- break;
449
- }
450
- const answer = (await ask(rl, ` ⚠ AID ${aid} 本地不存在,是否创建?[Y/n] `)).trim().toLowerCase();
451
- if (answer === 'n' || answer === 'no') {
452
- console.log(' 已跳过 AID 创建(启动时可能连接失败)');
453
- break;
376
+ const agentId = await pickAgentForChannel(rl);
377
+ if (!agentId)
378
+ return;
379
+ const agentConfig = loadAgent(agentId);
380
+ if (!agentConfig) {
381
+ console.error(`❌ 无法加载 agent ${agentId} 的配置`);
382
+ return;
454
383
  }
455
- // Create AID + agent.md via atomic ops
456
- console.log(' 正在创建 AID...');
457
- let failed = false;
458
- try {
459
- const result = await aidCreate(aid);
460
- console.log(` ✓ AID ${result.aid} 创建成功`);
461
- // Collect agent.md type and upload
462
- const typeInput = (await ask(rl, ' Agent 类型 human/ai [ai]: ')).trim().toLowerCase();
463
- const agentType = typeInput === 'human' ? 'human' : 'ai';
464
- const content = buildInitialAgentMd({ aid, type: agentType });
465
- try {
466
- await agentmdPut(content, { aid });
467
- console.log(' ✓ agent.md 已发布并写入本地');
468
- }
469
- catch (e) {
470
- console.log(` ⚠ agent.md 发布失败(首次连接将自动重试): ${String(e.message || e).slice(0, 100)}`);
471
- // Still write local copy as fallback
472
- try {
473
- const localDir = aidLocalDir(aid);
474
- fs.mkdirSync(localDir, { recursive: true });
475
- fs.writeFileSync(path.join(localDir, 'agent.md'), content, 'utf-8');
476
- console.log(' ✓ agent.md 已写入本地');
384
+ console.log(`\n📡 AUN 渠道配置 agent ${agentConfig.aid}`);
385
+ const current = agentConfig.owners?.[0];
386
+ if (current)
387
+ console.log(` 当前 Owner: ${current}`);
388
+ console.log(' Owner 将接收欢迎消息并拥有管理权限\n');
389
+ let owner = '';
390
+ while (!owner) {
391
+ const input = (await ask(rl, ` Owner AID${current ? ` [${current}]` : ''}: `)).trim();
392
+ if (!input) {
393
+ if (current) {
394
+ owner = current;
395
+ break;
477
396
  }
478
- catch (we) {
479
- console.log(` ✗ agent.md 本地写入失败: ${String(we.message || we).slice(0, 100)}`);
480
- failed = true;
481
- }
482
- }
483
- try {
484
- await result.client.close();
397
+ console.log(' ⚠ Owner AID 不能为空');
398
+ continue;
485
399
  }
486
- catch { /* ignore */ }
487
- try {
488
- result.store?.close();
400
+ if (!isValidAid(input)) {
401
+ console.log(' ⚠ Owner AID 格式无效(需合法多级域名,如 alice.agentid.pub)');
402
+ continue;
489
403
  }
490
- catch { /* ignore */ }
404
+ owner = input;
491
405
  }
492
- catch (e) {
493
- const msg = e.message || String(e);
494
- console.log(` ✗ AID 创建失败: ${msg.slice(0, 200)}`);
495
- failed = true;
496
- }
497
- if (!failed)
498
- break;
499
- // Creation failed — retry or give up
500
- const retry = (await ask(rl, ' → 重新输入 (r) / 跳过 (s) / 取消 (c)?[r/s/c] ')).trim().toLowerCase();
501
- if (retry === 'c')
502
- return null;
503
- if (retry === 's')
504
- break;
505
- // default: retry with new AID
406
+ // 写入顶层 owners:新 owner 置首位(getOwner 取 owners[0]),保留其余去重
407
+ agentConfig.owners = [owner, ...(agentConfig.owners || []).filter(o => o !== owner)];
408
+ saveAgent(agentConfig);
409
+ console.log(`\n✅ AUN 渠道 Owner 已设置: ${owner}`);
410
+ console.log(`\n重启生效: evolclaw restart`);
506
411
  }
507
- // Owner 必填
508
- console.log('\n📋 Owner 配置');
509
- console.log(' Owner 将接收欢迎消息并拥有管理权限');
510
- let owner = '';
511
- while (!owner) {
512
- const ownerInput = (await ask(rl, ' Owner AID (必填): ')).trim();
513
- if (!ownerInput) {
514
- console.log(' ⚠ Owner AID 不能为空');
515
- continue;
516
- }
517
- if (!isValidAid(ownerInput)) {
518
- console.log(' ⚠ Owner AID 格式无效');
519
- continue;
520
- }
521
- owner = ownerInput;
522
- console.log(` ✓ Owner 已设置: ${owner}`);
412
+ finally {
413
+ rl.close();
523
414
  }
524
- return { aid, owner };
525
415
  }
526
416
  // ==================== DingTalk ====================
527
417
  const DINGTALK_BASE_URL = 'https://oapi.dingtalk.com';