evolclaw 3.1.2 → 3.1.4

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 (48) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +2 -6
  3. package/assets/.env.template +4 -0
  4. package/assets/config.json.template +6 -0
  5. package/assets/wechat-group-qr.jpeg +0 -0
  6. package/dist/agents/claude-runner.js +1 -1
  7. package/dist/agents/codex-runner.js +75 -19
  8. package/dist/agents/gemini-runner.js +0 -2
  9. package/dist/agents/kit-renderer.js +85 -22
  10. package/dist/aun/aid/agentmd.js +67 -74
  11. package/dist/aun/aid/client.js +22 -7
  12. package/dist/aun/aid/identity.js +314 -28
  13. package/dist/aun/aid/index.js +2 -2
  14. package/dist/aun/rpc/connection.js +8 -10
  15. package/dist/channels/aun.js +53 -41
  16. package/dist/cli/agent.js +28 -28
  17. package/dist/cli/bench.js +8 -14
  18. package/dist/cli/help.js +23 -0
  19. package/dist/cli/index.js +398 -73
  20. package/dist/cli/init-channel.js +2 -3
  21. package/dist/cli/init.js +13 -6
  22. package/dist/cli/link-rules.js +2 -1
  23. package/dist/cli/net-check.js +10 -11
  24. package/dist/core/command-handler.js +621 -541
  25. package/dist/core/evolagent.js +31 -0
  26. package/dist/core/message/im-renderer.js +10 -0
  27. package/dist/core/message/message-bridge.js +123 -24
  28. package/dist/core/message/message-processor.js +61 -31
  29. package/dist/core/relation/peer-identity.js +64 -21
  30. package/dist/core/session/session-manager.js +191 -44
  31. package/dist/core/trigger/manager.js +37 -0
  32. package/dist/index.js +4 -1
  33. package/dist/paths.js +87 -16
  34. package/dist/utils/npm-ops.js +18 -11
  35. package/kits/eck_manifest.json +9 -9
  36. package/kits/rules/02-navigation.md +1 -0
  37. package/kits/rules/05-venue.md +2 -2
  38. package/kits/rules/06-channel.md +2 -18
  39. package/kits/templates/system-fragments/baseagent.md +8 -2
  40. package/kits/templates/system-fragments/channel.md +20 -8
  41. package/kits/templates/system-fragments/identity.md +5 -6
  42. package/kits/templates/system-fragments/relation.md +10 -5
  43. package/kits/templates/system-fragments/session.md +20 -0
  44. package/kits/templates/system-fragments/venue.md +5 -3
  45. package/package.json +4 -2
  46. package/dist/net-check.js +0 -640
  47. package/dist/watch-msg.js +0 -544
  48. package/kits/templates/system-fragments/runtime.md +0 -19
package/dist/cli/index.js CHANGED
@@ -11,6 +11,7 @@ import { migrateProject } from '../config-store.js';
11
11
  import { cmdInit } from './init.js';
12
12
  import { ipcQuery } from '../ipc.js';
13
13
  import { cmdInitWechat, cmdInitFeishu, cmdInitDingtalk, cmdInitQQBot, cmdInitWecom } from './init-channel.js';
14
+ import { isHelpFlag, wantsHelp } from './help.js';
14
15
  import * as platform from '../utils/cross-platform.js';
15
16
  import { EventBus } from '../core/event-bus.js';
16
17
  import { tryUpgrade, tryUpgradeAunSdk } from '../utils/npm-ops.js';
@@ -2739,6 +2740,7 @@ async function cmdCtl(args) {
2739
2740
  查询:
2740
2741
  status 查看会话状态
2741
2742
  check 检查渠道健康状态
2743
+ pwd 显示当前项目路径
2742
2744
  help 显示帮助
2743
2745
 
2744
2746
  配置:
@@ -2747,15 +2749,14 @@ async function cmdCtl(args) {
2747
2749
  compact 压缩当前会话上下文
2748
2750
  perm [mode] 查看/切换权限模式
2749
2751
 
2750
- 项目:
2751
- bind <path> 注册项目目录(不切换当前会话)
2752
-
2753
2752
  消息:
2754
2753
  send <消息内容> 主动发送文本消息(proactive 模式)
2755
2754
  file [channel] <path> 发送项目内文件
2756
2755
 
2756
+ Agent:
2757
+ agent <subcommand> EvolAgent 管理(list/show/new/enable/disable/reload/delete)
2758
+
2757
2759
  运维:
2758
- agentmd [put|set <内容>] 查看/管理 agent.md(仅 AUN 通道)
2759
2760
  restart [channel] 重启服务或重连指定渠道
2760
2761
 
2761
2762
  示例:
@@ -2765,7 +2766,7 @@ async function cmdCtl(args) {
2765
2766
  process.exit(1);
2766
2767
  }
2767
2768
  // help 不需要连接服务,直接复用无参数时的帮助输出
2768
- if (args[0] === 'help') {
2769
+ if (isHelpFlag(args[0])) {
2769
2770
  return cmdCtl([]);
2770
2771
  }
2771
2772
  const sessionId = process.env.EVOLCLAW_SESSION_ID;
@@ -2800,7 +2801,7 @@ async function cmdCtl(args) {
2800
2801
  async function cmdAgent(args) {
2801
2802
  const sub = args[0];
2802
2803
  const formatJson = args.includes('--format') && args[args.indexOf('--format') + 1] === 'json';
2803
- if (!sub || sub === 'help' || sub === '--help' || sub === '-h' || args.includes('--help') || args.includes('-h')) {
2804
+ if (!sub || isHelpFlag(sub)) {
2804
2805
  console.log(`用法: evolclaw agent <command>
2805
2806
 
2806
2807
  Commands:
@@ -2808,7 +2809,6 @@ Commands:
2808
2809
  show <aid> 查看 agent 详情(身份 + 配置 + 连接 + 会话 + 路径)
2809
2810
  new [aid] 交互式创建 agent
2810
2811
  new <aid> --non-interactive ... 非交互式创建
2811
- sync-aids 从本地 AID 批量创建 agent
2812
2812
  enable <aid> 启用 agent
2813
2813
  disable <aid> 停用 agent
2814
2814
  get <aid> <key> 读取单个配置字段(支持点路径)
@@ -2819,6 +2819,7 @@ Commands:
2819
2819
 
2820
2820
  Options:
2821
2821
  --format json 输出 JSON 格式
2822
+ --help, -h 各子命令均支持,查看详细用法
2822
2823
 
2823
2824
  示例:
2824
2825
  evolclaw agent list
@@ -2831,9 +2832,15 @@ Options:
2831
2832
  evolclaw agent delete mybot.agentid.pub --purge`);
2832
2833
  return;
2833
2834
  }
2834
- const { agentList, agentShow, agentCreateInteractive, agentCreateNonInteractive, agentSyncAids, agentReload, agentEnable, agentDisable, agentGet, agentSet, agentDelete, agentRename, } = await import('./agent.js');
2835
+ const { agentList, agentShow, agentCreateInteractive, agentCreateNonInteractive, agentReload, agentEnable, agentDisable, agentGet, agentSet, agentDelete, agentRename, } = await import('./agent.js');
2835
2836
  // --- list ---
2836
- if (!sub || sub === 'list') {
2837
+ if (sub === 'list') {
2838
+ if (wantsHelp(args)) {
2839
+ console.log(`用法: evolclaw agent list [--format json]
2840
+
2841
+ 列出所有 agent,显示名称、状态、渠道、项目、基座、最后活跃时间。`);
2842
+ return;
2843
+ }
2837
2844
  const result = await agentList();
2838
2845
  if (!result.ok) {
2839
2846
  if (formatJson) {
@@ -2894,6 +2901,24 @@ Options:
2894
2901
  }
2895
2902
  // --- new ---
2896
2903
  if (sub === 'new') {
2904
+ if (wantsHelp(args)) {
2905
+ console.log(`用法: evolclaw agent new [aid] 交互式创建
2906
+ evolclaw agent new <aid> --non-interactive [选项]
2907
+
2908
+ 非交互模式选项:
2909
+ --baseagent <claude|codex|gemini> 默认: PATH 中第一个可用
2910
+ --project <absolute path> 必填
2911
+ --owner <aid>
2912
+ --name <display-name>
2913
+ --description <text>
2914
+ --force 覆盖已有 config.json
2915
+ --format json 输出 JSON
2916
+
2917
+ 示例:
2918
+ evolclaw agent new mybot.agentid.pub
2919
+ evolclaw agent new mybot.agentid.pub --non-interactive --project /abs/path --baseagent claude`);
2920
+ return;
2921
+ }
2897
2922
  const name = args[1];
2898
2923
  const nonInteractive = args.includes('--non-interactive');
2899
2924
  if (nonInteractive) {
@@ -2966,38 +2991,38 @@ Options:
2966
2991
  }
2967
2992
  return;
2968
2993
  }
2969
- // --- sync-aids ---
2970
- if (sub === 'sync-aids') {
2971
- const result = await agentSyncAids();
2972
- if (!result.ok) {
2973
- if (formatJson) {
2974
- console.log(JSON.stringify(result));
2975
- }
2976
- else {
2977
- console.error(`❌ ${result.error}`);
2978
- }
2979
- process.exit(1);
2980
- }
2981
- if (formatJson) {
2982
- console.log(JSON.stringify(result, null, 2));
2983
- return;
2984
- }
2985
- if (result.created.length === 0) {
2986
- console.log('所有本地 AID 都已有对应 agent,无需同步。');
2987
- }
2988
- else {
2989
- console.log(`✓ 同步完成:新建 ${result.created.length} 个 agent(模板: ${result.template})`);
2990
- for (const aid of result.created)
2991
- console.log(` ✓ ${aid}`);
2992
- if (result.hotReloaded)
2993
- console.log(' ✓ 已热加载到运行中的进程');
2994
- else
2995
- console.log(' evolclaw 未运行,新 agent 将在下次启动时加载。');
2996
- }
2997
- return;
2998
- }
2994
+ // --- sync-aids (deprecated, commented out) ---
2995
+ // if (sub === 'sync-aids') {
2996
+ // const result = await agentSyncAids();
2997
+ // if (!result.ok) {
2998
+ // if (formatJson) { console.log(JSON.stringify(result)); }
2999
+ // else { console.error(`❌ ${result.error}`); }
3000
+ // process.exit(1);
3001
+ // }
3002
+ // if (formatJson) {
3003
+ // console.log(JSON.stringify(result, null, 2));
3004
+ // return;
3005
+ // }
3006
+ // if (result.created.length === 0) {
3007
+ // console.log('所有本地 AID 都已有对应 agent,无需同步。');
3008
+ // } else {
3009
+ // console.log(`✓ 同步完成:新建 ${result.created.length} 个 agent(模板: ${result.template})`);
3010
+ // for (const aid of result.created) console.log(` ✓ ${aid}`);
3011
+ // if (result.hotReloaded) console.log(' 已热加载到运行中的进程');
3012
+ // else console.log(' evolclaw 未运行,新 agent 将在下次启动时加载。');
3013
+ // }
3014
+ // return;
3015
+ // }
2999
3016
  // --- reload ---
3000
3017
  if (sub === 'reload') {
3018
+ if (wantsHelp(args)) {
3019
+ console.log(`用法: evolclaw agent reload [aid] [--format json]
3020
+
3021
+ 热重载 agent 配置。
3022
+ 无参数 全量 resync(扫磁盘,新增上线、删除下线、修改热更新)
3023
+ <aid> 仅热重载指定 agent`);
3024
+ return;
3025
+ }
3001
3026
  const target = args[1] && !args[1].startsWith('--') ? args[1] : undefined;
3002
3027
  const result = await agentReload(target);
3003
3028
  if (!result.ok) {
@@ -3025,6 +3050,12 @@ Options:
3025
3050
  }
3026
3051
  // --- enable ---
3027
3052
  if (sub === 'enable') {
3053
+ if (wantsHelp(args)) {
3054
+ console.log(`用法: evolclaw agent enable <aid> [--format json]
3055
+
3056
+ 启用 agent。若服务运行中会热重载,否则下次 evolclaw start 时生效。`);
3057
+ return;
3058
+ }
3028
3059
  const aid = args[1];
3029
3060
  if (!aid) {
3030
3061
  console.error('用法: evolclaw agent enable <aid>');
@@ -3050,6 +3081,12 @@ Options:
3050
3081
  }
3051
3082
  // --- disable ---
3052
3083
  if (sub === 'disable') {
3084
+ if (wantsHelp(args)) {
3085
+ console.log(`用法: evolclaw agent disable <aid> [--format json]
3086
+
3087
+ 停用 agent。若服务运行中会热重载离线,否则在配置中标记为禁用。`);
3088
+ return;
3089
+ }
3053
3090
  const aid = args[1];
3054
3091
  if (!aid) {
3055
3092
  console.error('用法: evolclaw agent disable <aid>');
@@ -3075,6 +3112,16 @@ Options:
3075
3112
  }
3076
3113
  // --- get ---
3077
3114
  if (sub === 'get') {
3115
+ if (wantsHelp(args)) {
3116
+ console.log(`用法: evolclaw agent get <aid> <key> [--format json]
3117
+
3118
+ 读取单个配置字段。key 支持点路径,如 "channels.aun.enabled"。
3119
+
3120
+ 示例:
3121
+ evolclaw agent get mybot.agentid.pub active_baseagent
3122
+ evolclaw agent get mybot.agentid.pub channels.aun.enabled`);
3123
+ return;
3124
+ }
3078
3125
  const aid = args[1];
3079
3126
  const key = args[2];
3080
3127
  if (!aid || !key) {
@@ -3102,6 +3149,16 @@ Options:
3102
3149
  }
3103
3150
  // --- set ---
3104
3151
  if (sub === 'set') {
3152
+ if (wantsHelp(args)) {
3153
+ console.log(`用法: evolclaw agent set <aid> <key> <value> [--format json]
3154
+
3155
+ 修改单个配置字段。key 支持点路径。修改后若服务运行中会自动热重载。
3156
+
3157
+ 示例:
3158
+ evolclaw agent set mybot.agentid.pub active_baseagent codex
3159
+ evolclaw agent set mybot.agentid.pub channels.aun.enabled true`);
3160
+ return;
3161
+ }
3105
3162
  const aid = args[1];
3106
3163
  const key = args[2];
3107
3164
  const val = args[3];
@@ -3129,6 +3186,15 @@ Options:
3129
3186
  }
3130
3187
  // --- rename ---
3131
3188
  if (sub === 'rename') {
3189
+ if (wantsHelp(args)) {
3190
+ console.log(`用法: evolclaw agent rename <aid> <name> [--format json]
3191
+
3192
+ 修改 agent 显示名称。同时更新本地 agent.md 并尝试重新上传。
3193
+
3194
+ 示例:
3195
+ evolclaw agent rename mybot.agentid.pub "My Bot"`);
3196
+ return;
3197
+ }
3132
3198
  const aid = args[1];
3133
3199
  const newName = args[2];
3134
3200
  if (!aid || !newName) {
@@ -3155,6 +3221,14 @@ Options:
3155
3221
  }
3156
3222
  // --- delete ---
3157
3223
  if (sub === 'delete') {
3224
+ if (wantsHelp(args)) {
3225
+ console.log(`用法: evolclaw agent delete <aid> [--purge] [--format json]
3226
+
3227
+ 删除 agent 的配置。
3228
+ --purge 同时清除该 agent 的会话、消息、日志等运行时数据
3229
+ 默认 仅删除 config.json,运行时数据保留`);
3230
+ return;
3231
+ }
3158
3232
  const aid = args[1];
3159
3233
  if (!aid) {
3160
3234
  console.error('用法: evolclaw agent delete <aid> [--purge]');
@@ -3181,6 +3255,12 @@ Options:
3181
3255
  }
3182
3256
  // --- show ---
3183
3257
  if (sub === 'show') {
3258
+ if (wantsHelp(args)) {
3259
+ console.log(`用法: evolclaw agent show <aid> [--format json]
3260
+
3261
+ 查看 agent 详情:身份、配置、连接状态、会话路径等。`);
3262
+ return;
3263
+ }
3184
3264
  const aid = args[1];
3185
3265
  if (!aid) {
3186
3266
  console.error('用法: evolclaw agent show <aid>');
@@ -3294,63 +3374,134 @@ function resolveAunPath(args) {
3294
3374
  return process.env.AUN_HOME || undefined;
3295
3375
  }
3296
3376
  async function cmdAid(args) {
3297
- const sub = args[0] || 'list';
3377
+ const sub = args[0];
3298
3378
  const formatJson = args.includes('--format') && args[args.indexOf('--format') + 1] === 'json';
3299
3379
  const aunPath = resolveAunPath(args);
3300
- if (!sub || sub === 'help' || sub === '--help' || sub === '-h' || args.includes('--help') || args.includes('-h')) {
3380
+ if (!sub || isHelpFlag(sub)) {
3301
3381
  console.log(`用法: evolclaw aid <command>
3302
3382
 
3303
3383
  Commands:
3304
- list 列出本地所有 AID
3305
- show <aid> 查看本地 AID 详情(证书有效期、私钥状态)
3384
+ list 列出本地所有 AID(实测 sign+verify)
3385
+ show <aid> 查看本地 AID 详情(证书、私钥、签名能力)
3306
3386
  new <aid> 创建新 AID 身份
3307
- delete <aid> 删除本地 AID(无网络注销)
3387
+ delete <aid> 删除指定本地 AID(无网络注销)
3388
+ delete --orphan 批量清理无私钥的外部 AID 缓存
3389
+ delete --no-cert 批量清理无私钥也无公钥证书的孤儿目录
3390
+ delete --unrecoverable 批量清理云端公钥已变更、本地不可恢复的 AID
3391
+ 批量删除默认 dry-run,加 --yes 执行
3308
3392
  lookup <aid> 远程探测 AID(是否存在 + 网关 + agent.md)
3309
3393
  agentmd put <aid> 读本地 agent.md → 签名 → 上传
3310
3394
  agentmd get <aid> 下载 agent.md → 验签 → 本地持久化
3311
3395
 
3312
3396
  Options:
3313
3397
  --format json 输出 JSON 格式
3398
+ --help, -h 各子命令均支持,查看详细用法
3314
3399
 
3315
3400
  示例:
3316
3401
  evolclaw aid list
3317
3402
  evolclaw aid show toleiliang2.agentid.pub
3318
3403
  evolclaw aid new reviewer.agentid.pub
3404
+ evolclaw aid delete --help
3319
3405
  evolclaw aid delete old.agentid.pub
3406
+ evolclaw aid delete --orphan
3407
+ evolclaw aid delete --unrecoverable --yes
3320
3408
  evolclaw aid lookup someone.agentid.pub
3321
3409
  evolclaw aid agentmd put mybot.agentid.pub
3322
3410
  evolclaw aid agentmd get someone.agentid.pub`);
3323
3411
  return;
3324
3412
  }
3325
- const { aidList, aidCreate, aidShow, aidDelete, aidLookup, agentmdPut, agentmdGet, buildInitialAgentMd, isValidAid } = await import('../aun/aid/index.js');
3413
+ const { aidList, aidListVerified, aidCreate, aidShow, aidDelete, aidLookup, agentmdPut, agentmdGet, buildInitialAgentMd, isValidAid } = await import('../aun/aid/index.js');
3326
3414
  if (sub === 'list') {
3327
- const aids = aidList(aunPath);
3415
+ if (wantsHelp(args)) {
3416
+ console.log(`用法: evolclaw aid list [筛选选项] [--no-verify] [--format json]
3417
+
3418
+ 列出本地 AID 并跑 sign+verify 自检。
3419
+
3420
+ 筛选选项(可组合,不指定 = 列出 mine + broken + peer-cert):
3421
+ --mine 仅本地可用身份(实测可签名+验签通过)
3422
+ --broken 仅有私钥但不可用(公钥不匹配 / 证书过期 / sign 失败)
3423
+ --peer-cert 仅对端 AID(无私钥,有公钥证书)
3424
+ --no-cert 仅无私钥无证书的目录(默认隐藏,需用 aid delete --no-cert 清理)
3425
+
3426
+ 选项:
3427
+ --no-verify 跳过 sign+verify 实测,仅静态扫描(更快,mine/broken 仅按静态判定近似)
3428
+ --format json JSON 格式输出
3429
+
3430
+ 输出图标:
3431
+ 🔑 有私钥
3432
+ ✅ 实测可签名/验签
3433
+ ❌ 不可签名(公钥不匹配 / sign 失败 / verify 失败等)
3434
+ ⌛ 证书过期
3435
+ 📜 有公钥证书
3436
+ 📄 有 agent.md
3437
+
3438
+ 示例:
3439
+ evolclaw aid list 列出 mine + broken + peer-cert
3440
+ evolclaw aid list --mine 仅可用身份
3441
+ evolclaw aid list --mine --broken 所有有私钥的 AID
3442
+ evolclaw aid list --no-cert 仅无私钥无证书的孤儿目录
3443
+ evolclaw aid list --no-verify 跳过实测,快速静态扫描`);
3444
+ return;
3445
+ }
3446
+ const wantMine = args.includes('--mine');
3447
+ const wantBroken = args.includes('--broken');
3448
+ const wantPeerCert = args.includes('--peer-cert');
3449
+ const wantNoCert = args.includes('--no-cert');
3450
+ const noVerify = args.includes('--no-verify');
3451
+ const anyFilter = wantMine || wantBroken || wantPeerCert || wantNoCert;
3452
+ // 默认: mine + broken + peer-cert(隐藏 no-cert,需显式 --no-cert 才列)
3453
+ const showMine = anyFilter ? wantMine : true;
3454
+ const showBroken = anyFilter ? wantBroken : true;
3455
+ const showPeerCert = anyFilter ? wantPeerCert : true;
3456
+ const showNoCert = anyFilter ? wantNoCert : false;
3457
+ const all = noVerify ? aidList(aunPath) : await aidListVerified(aunPath);
3458
+ const aids = all.filter(a => (showMine && a.category === 'mine') ||
3459
+ (showBroken && a.category === 'broken') ||
3460
+ (showPeerCert && a.category === 'peer-cert') ||
3461
+ (showNoCert && a.category === 'no-cert'));
3328
3462
  if (formatJson) {
3329
3463
  console.log(JSON.stringify(aids, null, 2));
3330
3464
  return;
3331
3465
  }
3332
3466
  if (aids.length === 0) {
3333
- console.log('本地无 AID');
3467
+ console.log('无匹配 AID');
3334
3468
  return;
3335
3469
  }
3336
- console.log('本地 AID:');
3470
+ console.log(`本地 AID${noVerify ? '(静态扫描,未实测)' : ''}:`);
3337
3471
  for (const a of aids) {
3338
- const icons = [
3339
- a.hasPrivateKey ? '🔑' : ' ',
3340
- a.hasAgentMd ? '📄' : ' ',
3341
- ].join('');
3342
- console.log(` ${icons} ${a.aid}`);
3343
- }
3344
- console.log('\n🔑=私钥 📄=agent.md');
3472
+ const keyIcon = a.hasPrivateKey ? '🔑' : ' ';
3473
+ let signIcon = ' ';
3474
+ // --no-verify signVerified 始终为 null,用 canSign 作为静态近似
3475
+ const effectiveOk = noVerify ? a.canSign : a.signVerified === true;
3476
+ const effectiveFail = noVerify ? (a.hasPrivateKey && !a.canSign) : (a.hasPrivateKey && a.signVerified === false);
3477
+ if (effectiveOk)
3478
+ signIcon = '';
3479
+ else if (a.hasPrivateKey && a.certExpired)
3480
+ signIcon = '⌛';
3481
+ else if (effectiveFail)
3482
+ signIcon = '❌';
3483
+ const certIcon = a.hasCert ? '📜' : ' ';
3484
+ const mdIcon = a.hasAgentMd ? '📄' : ' ';
3485
+ const tail = !noVerify && a.signVerified === false && a.signError && !(a.keyMatchesCert === false || a.certExpired || !a.hasPrivateKey || !a.hasCert)
3486
+ ? ` (${a.signError})` : '';
3487
+ console.log(` ${keyIcon} ${signIcon} ${certIcon} ${mdIcon} ${a.aid}${tail}`);
3488
+ }
3489
+ console.log('\n🔑=私钥 ✅=可签名/验签 ❌=不可签名 ⌛=证书过期 📜=公钥证书 📄=agent.md');
3345
3490
  return;
3346
3491
  }
3347
3492
  if (sub === 'show') {
3493
+ if (wantsHelp(args)) {
3494
+ console.log(`用法: evolclaw aid show <aid> [--format json]
3495
+
3496
+ 查看本地 AID 详情:私钥/证书/agent.md 状态、签名能力实测。`);
3497
+ return;
3498
+ }
3348
3499
  const aid = args[1];
3349
3500
  if (!aid) {
3350
3501
  console.error('用法: evolclaw aid show <aid>');
3351
3502
  process.exit(1);
3352
3503
  }
3353
- const info = aidShow(aid, { aunPath });
3504
+ const info = await aidShow(aid, { aunPath });
3354
3505
  if (formatJson) {
3355
3506
  console.log(JSON.stringify(info, null, 2));
3356
3507
  return;
@@ -3358,12 +3509,37 @@ Options:
3358
3509
  console.log(`AID: ${info.aid}`);
3359
3510
  console.log(` 私钥: ${info.hasPrivateKey ? '有' : '无'}`);
3360
3511
  console.log(` agent.md: ${info.hasAgentMd ? '有' : '无'}`);
3361
- console.log(` 证书到期: ${info.certExpiresAt ?? '无证书'}`);
3512
+ if (info.hasAgentMd) {
3513
+ const sigLabel = info.agentMdSignature === 'verified' ? '✓ 已验签'
3514
+ : info.agentMdSignature === 'unsigned' ? '⚠ 未签名'
3515
+ : info.agentMdSignature === 'invalid' ? `✗ 签名无效${info.agentMdSignatureReason ? ': ' + info.agentMdSignatureReason : ''}`
3516
+ : '? 未知';
3517
+ console.log(` 签名状态: ${sigLabel}`);
3518
+ }
3519
+ console.log(` 证书到期: ${info.certExpiresAt ?? '无证书'}${info.certExpired ? ' (已过期!)' : ''}`);
3362
3520
  if (info.certSubject)
3363
3521
  console.log(` 证书主体: ${info.certSubject}`);
3522
+ if (info.keyMatchesCert === false)
3523
+ console.log(` 密钥/证书: ✗ 公钥不匹配(cert.pem 与 key.json 公钥不一致)`);
3524
+ else if (info.keyMatchesCert === true)
3525
+ console.log(` 密钥/证书: ✓ 公钥一致`);
3526
+ if (info.signVerified === true)
3527
+ console.log(` 可签名/验签: ✓ 实测通过`);
3528
+ else if (info.signVerified === false)
3529
+ console.log(` 可签名/验签: ✗ 失败${info.signError ? `(${info.signError})` : ''}`);
3530
+ else
3531
+ console.log(` 可签名/验签: ? 未知`);
3364
3532
  return;
3365
3533
  }
3366
3534
  if (sub === 'new') {
3535
+ if (wantsHelp(args)) {
3536
+ console.log(`用法: evolclaw aid new <完整AID>
3537
+
3538
+ 创建新 AID 身份:生成 ECDSA 密钥对、向 Issuer 申请证书、构建并上传初始 agent.md。
3539
+
3540
+ 例: evolclaw aid new reviewer.agentid.pub`);
3541
+ return;
3542
+ }
3367
3543
  const aid = args[1];
3368
3544
  if (!aid) {
3369
3545
  console.error('用法: evolclaw aid new <完整AID>\n例: evolclaw aid new reviewer.agentid.pub');
@@ -3394,22 +3570,162 @@ Options:
3394
3570
  return;
3395
3571
  }
3396
3572
  if (sub === 'delete') {
3397
- const aid = args[1];
3398
- if (!aid) {
3399
- console.error('用法: evolclaw aid delete <aid>');
3573
+ if (wantsHelp(args)) {
3574
+ console.log(`用法: evolclaw aid delete <子命令>
3575
+
3576
+ 单个删除:
3577
+ evolclaw aid delete <aid> 删除指定 AID 的本地数据(无网络注销)
3578
+
3579
+ 批量删除(默认 dry-run,加 --yes 才真删):
3580
+ evolclaw aid delete --orphan 删除所有"无私钥"的本地缓存(外部 AID)
3581
+ evolclaw aid delete --no-cert 删除所有"无私钥也无公钥证书"的目录
3582
+ 条件:!hasPrivateKey && !hasCert
3583
+ 这些目录最多只剩 agent.md 或 SQLite 残留,
3584
+ 对验签和加密通信都没用,删除安全。
3585
+ evolclaw aid delete --unrecoverable 删除所有不可恢复的 AID
3586
+ 条件:本地 sign+verify 实测失败
3587
+ 且 PKI 探测确认云端公钥也不等本地 key.json
3588
+
3589
+ 选项:
3590
+ --yes 跳过 dry-run,立即执行
3591
+ --skip-pki --unrecoverable 时跳过 PKI 探测,仅依据本地 sign+verify 失败判断(危险,可能误删可恢复 AID)
3592
+ --format json 输出 JSON 格式
3593
+
3594
+ 示例:
3595
+ evolclaw aid delete old.agentid.pub
3596
+ evolclaw aid delete --orphan 列出会被清理的孤儿
3597
+ evolclaw aid delete --orphan --yes 实际清理
3598
+ evolclaw aid delete --no-cert 列出无证书孤儿目录
3599
+ evolclaw aid delete --no-cert --yes 实际清理
3600
+ evolclaw aid delete --unrecoverable 联网探测后列出无救 AID
3601
+ evolclaw aid delete --unrecoverable --yes`);
3602
+ return;
3603
+ }
3604
+ const yes = args.includes('--yes');
3605
+ const skipPki = args.includes('--skip-pki');
3606
+ const orphan = args.includes('--orphan');
3607
+ const noCert = args.includes('--no-cert');
3608
+ const unrecoverable = args.includes('--unrecoverable');
3609
+ const modes = [orphan, noCert, unrecoverable].filter(Boolean).length;
3610
+ if (modes > 1) {
3611
+ console.error('❌ --orphan / --no-cert / --unrecoverable 互斥,不能同时使用');
3400
3612
  process.exit(1);
3401
3613
  }
3402
- const deleted = aidDelete(aid, { aunPath });
3403
- if (deleted) {
3404
- console.log(`✓ ${aid} 已删除`);
3614
+ // 单个 aid 删除:保留原有行为
3615
+ if (modes === 0) {
3616
+ const aid = args[1];
3617
+ if (!aid) {
3618
+ console.error('用法: evolclaw aid delete <aid>\n evolclaw aid delete --orphan | --no-cert | --unrecoverable [--yes]\n evolclaw aid delete --help 查看完整用法');
3619
+ process.exit(1);
3620
+ }
3621
+ const deleted = aidDelete(aid, { aunPath });
3622
+ if (deleted) {
3623
+ console.log(`✓ ${aid} 已删除`);
3624
+ }
3625
+ else {
3626
+ console.error(`❌ 本地不存在: ${aid}`);
3627
+ process.exit(1);
3628
+ }
3629
+ return;
3630
+ }
3631
+ // 批量模式:先选出候选
3632
+ const { probePkiRecoverability } = await import('../aun/aid/index.js');
3633
+ const candidates = [];
3634
+ if (orphan) {
3635
+ const aids = aidList(aunPath);
3636
+ for (const a of aids) {
3637
+ if (!a.hasPrivateKey)
3638
+ candidates.push({ aid: a.aid, reason: 'no private key (external AID cache)' });
3639
+ }
3640
+ }
3641
+ else if (noCert) {
3642
+ const aids = aidList(aunPath);
3643
+ for (const a of aids) {
3644
+ if (!a.hasPrivateKey && !a.hasCert) {
3645
+ const traits = [a.hasAgentMd ? 'agent.md' : null].filter(Boolean).join(', ');
3646
+ candidates.push({ aid: a.aid, reason: `no private key, no cert${traits ? ` (only: ${traits})` : ''}` });
3647
+ }
3648
+ }
3405
3649
  }
3406
3650
  else {
3407
- console.error(`❌ 本地不存在: ${aid}`);
3408
- process.exit(1);
3651
+ // unrecoverable: 必须先做 sign+verify 实测
3652
+ if (!formatJson)
3653
+ console.log('扫描中: 本地签名/验签实测...');
3654
+ const aids = await aidListVerified(aunPath);
3655
+ const localBroken = aids.filter(a => a.hasPrivateKey && a.signVerified === false);
3656
+ if (skipPki) {
3657
+ for (const a of localBroken) {
3658
+ candidates.push({ aid: a.aid, reason: `sign+verify failed (${a.signError ?? 'unknown'}) [--skip-pki: 未联网验证]` });
3659
+ }
3660
+ }
3661
+ else {
3662
+ if (!formatJson)
3663
+ console.log(`扫描中: 对 ${localBroken.length} 个本地损坏 AID 做 PKI 探测...`);
3664
+ for (const a of localBroken) {
3665
+ const r = await probePkiRecoverability(a.aid, { aunPath });
3666
+ if (r.kind === 'unrecoverable') {
3667
+ candidates.push({ aid: a.aid, reason: `local broken; PKI: ${r.reason}`, pki: 'unrecoverable' });
3668
+ }
3669
+ else if (r.kind === 'no-server-record') {
3670
+ candidates.push({ aid: a.aid, reason: `local broken; PKI: ${r.reason}`, pki: 'no-server-record' });
3671
+ }
3672
+ else {
3673
+ // recoverable / no-key / unknown 一律保守不删
3674
+ if (!formatJson)
3675
+ console.log(` · 跳过 ${a.aid}: PKI=${r.kind}${('reason' in r) ? ' — ' + r.reason : ''}`);
3676
+ }
3677
+ }
3678
+ }
3679
+ }
3680
+ if (formatJson) {
3681
+ console.log(JSON.stringify({
3682
+ mode: orphan ? 'orphan' : noCert ? 'no-cert' : 'unrecoverable',
3683
+ dryRun: !yes,
3684
+ skipPki: unrecoverable ? skipPki : undefined,
3685
+ candidates,
3686
+ }, null, 2));
3687
+ if (yes) {
3688
+ for (const c of candidates)
3689
+ aidDelete(c.aid, { aunPath });
3690
+ }
3691
+ return;
3692
+ }
3693
+ if (candidates.length === 0) {
3694
+ console.log(orphan ? '✓ 无孤儿 AID' : noCert ? '✓ 无无证书孤儿目录' : '✓ 无不可恢复 AID');
3695
+ return;
3696
+ }
3697
+ console.log(`\n${yes ? '将删除' : '候选删除(dry-run)'}:${candidates.length} 个 AID`);
3698
+ for (const c of candidates) {
3699
+ console.log(` - ${c.aid}`);
3700
+ console.log(` ${c.reason}`);
3701
+ }
3702
+ if (!yes) {
3703
+ console.log('\n(dry-run,未真删除。加 --yes 执行真删。)');
3704
+ return;
3705
+ }
3706
+ let ok = 0;
3707
+ let fail = 0;
3708
+ for (const c of candidates) {
3709
+ const deleted = aidDelete(c.aid, { aunPath });
3710
+ if (deleted) {
3711
+ console.log(` ✓ 删除 ${c.aid}`);
3712
+ ok++;
3713
+ }
3714
+ else {
3715
+ console.log(` ✗ 失败 ${c.aid}(已不存在?)`);
3716
+ fail++;
3717
+ }
3409
3718
  }
3719
+ console.log(`\n完成:成功 ${ok},失败 ${fail}`);
3410
3720
  return;
3411
3721
  }
3412
3722
  if (sub === 'lookup') {
3723
+ if (wantsHelp(args)) {
3724
+ console.log(`用法: evolclaw aid lookup <aid> [--format json]
3725
+
3726
+ 远程探测 AID:是否注册、所在网关、是否有 agent.md(不验签,仅获取)。`);
3727
+ return;
3728
+ }
3413
3729
  const aid = args[1];
3414
3730
  if (!aid) {
3415
3731
  console.error('用法: evolclaw aid lookup <aid>');
@@ -3447,6 +3763,13 @@ Options:
3447
3763
  if (sub === 'agentmd') {
3448
3764
  const verb = args[1];
3449
3765
  const aid = args[2];
3766
+ if (!verb || isHelpFlag(verb) || wantsHelp(args)) {
3767
+ console.log(`用法: evolclaw aid agentmd <put|get> <aid> [--format json]
3768
+
3769
+ put <aid> 读本地 agent.md → 用本地私钥签名 → 上传到 PKI
3770
+ get <aid> 从 PKI 下载 agent.md → 验签 → 持久化到本地`);
3771
+ return;
3772
+ }
3450
3773
  if (verb === 'put') {
3451
3774
  if (!aid) {
3452
3775
  console.error('用法: evolclaw aid agentmd put <aid>');
@@ -3518,7 +3841,7 @@ Options:
3518
3841
  }
3519
3842
  // ==================== RPC ====================
3520
3843
  async function cmdRpc(args) {
3521
- if (args[0] === 'help' || args.length === 0) {
3844
+ if (args.length === 0 || isHelpFlag(args[0])) {
3522
3845
  console.log(`用法: evolclaw rpc --as <aid> --params <params>
3523
3846
 
3524
3847
  通用 AUN RPC 调用。
@@ -3597,7 +3920,7 @@ async function cmdStorage(args) {
3597
3920
  const sub = args[0];
3598
3921
  const aunPath = resolveAunPath(args);
3599
3922
  const formatJson = args.includes('--format') && args[args.indexOf('--format') + 1] === 'json';
3600
- if (!sub || sub === 'help') {
3923
+ if (!sub || isHelpFlag(sub)) {
3601
3924
  console.log(`用法: evolclaw storage <command> <aid> [options]
3602
3925
 
3603
3926
  Commands:
@@ -3747,7 +4070,7 @@ async function cmdMsg(args) {
3747
4070
  const appIdx = args.indexOf('--app');
3748
4071
  const appSlot = appIdx >= 0 ? args[appIdx + 1] : undefined;
3749
4072
  const asDaemon = args.includes('--as-daemon');
3750
- if (!sub || sub === 'help') {
4073
+ if (!sub || isHelpFlag(sub)) {
3751
4074
  console.log(`用法: evolclaw msg <command> <from-aid> [args...] [options]
3752
4075
 
3753
4076
  Commands:
@@ -4033,7 +4356,7 @@ async function cmdGroup(args) {
4033
4356
  const appIdx = args.indexOf('--app');
4034
4357
  const appSlot = appIdx >= 0 ? args[appIdx + 1] : undefined;
4035
4358
  const asDaemon = args.includes('--as-daemon');
4036
- if (!sub || sub === 'help') {
4359
+ if (!sub || isHelpFlag(sub)) {
4037
4360
  console.log(`用法: evolclaw group <command> <from-aid> [args...] [options]
4038
4361
 
4039
4362
  消息:
@@ -4433,7 +4756,7 @@ export async function main(args) {
4433
4756
  }
4434
4757
  switch (cmd) {
4435
4758
  case 'init':
4436
- if (args[1] === 'help') {
4759
+ if (isHelpFlag(args[1])) {
4437
4760
  console.log(`用法: evolclaw init [渠道] [选项]
4438
4761
 
4439
4762
  仅初始化 defaults.json:
@@ -4509,7 +4832,7 @@ export async function main(args) {
4509
4832
  await cmdWatchAid();
4510
4833
  }
4511
4834
  else if (args[1] === 'msg') {
4512
- if (args[2] === '--help' || args[2] === '-h' || args[2] === 'help') {
4835
+ if (isHelpFlag(args[2])) {
4513
4836
  console.log(`用法: evolclaw watch msg
4514
4837
 
4515
4838
  三面板交互式消息监控 TUI。
@@ -4645,17 +4968,19 @@ Commands:
4645
4968
  --name <display-name>
4646
4969
  --description <text>
4647
4970
  --force (覆盖已有 config.json)
4648
- agent sync-aids 从本地 AID 批量同步创建 agent(以最早 agent 为模板)
4649
4971
  agent reload 全量 resync(扫磁盘,新增上线、删除下线、修改热更新)
4650
4972
  agent reload <n> 热重载指定 agent 配置
4651
4973
  aid AID 身份管理
4652
4974
  aid list 列出本地所有 AID
4653
4975
  aid show <aid> 查看本地 AID 详情(证书有效期、私钥状态)
4654
4976
  aid new <aid> 创建新 AID 身份
4655
- aid delete <aid> 删除本地 AID
4656
4977
  aid lookup <aid> 远程探测 AID(是否存在 + 网关 + agent.md)
4657
4978
  aid agentmd put <aid> 签名并上传 agent.md
4658
4979
  aid agentmd get <aid> 下载并验签 agent.md
4980
+ aid delete <aid> 删除指定 AID
4981
+ aid delete --orphan 清理无私钥的外部 AID 缓存
4982
+ aid delete --unrecoverable 清理云端公钥已变更、不可恢复的 AID
4983
+ 默认 dry-run,加 --yes 执行
4659
4984
  net 网络链路诊断
4660
4985
  net check [<aid>] 10 步链路检测(DNS→Discovery→TCP→TLS→WSS→Auth→Ping→Echo)
4661
4986
  net help 查看详细帮助