evolcore 0.0.2 → 0.0.3

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 (124) hide show
  1. package/CHANGELOG.md +44 -793
  2. package/dist/agents/claude-runner.js +197 -17
  3. package/dist/agents/codex-runner.js +46 -3
  4. package/dist/aun/outbox.js +8 -0
  5. package/dist/channels/aun.js +21 -4
  6. package/dist/channels/contact-bind-code.js +134 -0
  7. package/dist/channels/dingtalk.js +979 -149
  8. package/dist/channels/feishu.js +130 -54
  9. package/dist/channels/wecom-card.js +101 -0
  10. package/dist/channels/wecom-onboarding.js +82 -0
  11. package/dist/channels/wecom-state.js +191 -0
  12. package/dist/channels/wecom.js +755 -163
  13. package/dist/cli/agent-command.js +2 -1
  14. package/dist/cli/aun-commands.js +88 -33
  15. package/dist/cli/bench.js +2 -2
  16. package/dist/cli/contact.js +71 -0
  17. package/dist/cli/ctl-command.js +2 -2
  18. package/dist/cli/daemon-commands.js +77 -214
  19. package/dist/cli/handoff-command.js +2 -2
  20. package/dist/cli/help.js +9 -5
  21. package/dist/cli/index.js +132 -116
  22. package/dist/cli/init-channel.js +92 -97
  23. package/dist/cli/init.js +63 -26
  24. package/dist/cli/model.js +2 -1
  25. package/dist/cli/net-check.js +2 -2
  26. package/dist/cli/queue-command.js +30 -6
  27. package/dist/cli/raw-key-input.js +25 -0
  28. package/dist/cli/response.js +5 -6
  29. package/dist/cli/restart-monitor.js +25 -1
  30. package/dist/cli/stats.js +6 -4
  31. package/dist/cli/trigger-command.js +55 -15
  32. package/dist/cli/version.js +6 -1
  33. package/dist/config/builtin-role-templates.js +22 -10
  34. package/dist/config/builtin-roles.js +7 -1
  35. package/dist/config/config-manager.js +221 -17
  36. package/dist/config/contact-alias.js +68 -0
  37. package/dist/config/contact-book-store.js +454 -0
  38. package/dist/config/contact-book-v2-startup.js +35 -0
  39. package/dist/config/contact-book.js +156 -303
  40. package/dist/config/contact-operation-service.js +110 -0
  41. package/dist/config/peer-role-resolver.js +133 -54
  42. package/dist/config/role-ranks.js +18 -0
  43. package/dist/config/role-service.js +16 -19
  44. package/dist/config/role-store.js +16 -5
  45. package/dist/config/roles.js +10 -1
  46. package/dist/config-store.js +0 -2
  47. package/dist/core/auth/authorization-audit.js +10 -1
  48. package/dist/core/auth/operation-authorizer.js +2 -0
  49. package/dist/core/auth/operation-catalog.js +56 -0
  50. package/dist/core/command/command-handler.js +105 -10
  51. package/dist/core/command/connect-menu.js +374 -0
  52. package/dist/core/command/menu-handler.js +114 -16
  53. package/dist/core/command/role-menu.js +128 -29
  54. package/dist/core/command/slash-handler.js +28 -18
  55. package/dist/core/daemon-file-cache.js +12 -6
  56. package/dist/core/event-catalog.js +70 -0
  57. package/dist/core/evolagent-registry.js +0 -1
  58. package/dist/core/evolagent.js +20 -9
  59. package/dist/core/message/im-renderer.js +2 -0
  60. package/dist/core/message/message-bridge.js +79 -8
  61. package/dist/core/message/message-queue.js +10 -0
  62. package/dist/core/message/message-utils.js +8 -2
  63. package/dist/core/message/response-engine.js +269 -25
  64. package/dist/core/message/send-receipt.js +24 -0
  65. package/dist/core/message/stream-debouncer.js +11 -2
  66. package/dist/core/permission/tool-policy.js +47 -15
  67. package/dist/core/protected-paths.js +2 -0
  68. package/dist/core/session/session-fs-store.js +44 -3
  69. package/dist/core/session/session-manager.js +61 -5
  70. package/dist/index.js +62 -6
  71. package/dist/ipc.js +34 -5
  72. package/dist/stats/billing.js +20 -8
  73. package/dist/trigger/manager.js +3 -0
  74. package/dist/trigger/parser.js +77 -2
  75. package/dist/trigger/patch.js +8 -1
  76. package/dist/trigger/scheduler.js +3 -1
  77. package/dist/trigger/validation.js +13 -0
  78. package/dist/utils/aid-bind.js +43 -29
  79. package/dist/utils/instance-registry.js +14 -7
  80. package/dist/utils/log-writer.js +46 -0
  81. package/dist/utils/media-cache.js +4 -1
  82. package/dist/utils/model-prices.jsonl +6 -3
  83. package/dist/utils/restart-safety.js +31 -0
  84. package/dist/utils/system-memory.js +62 -0
  85. package/kits/docs/INDEX.md +2 -1
  86. package/kits/docs/evolcore/INDEX.md +5 -3
  87. package/kits/docs/evolcore/agent.md +9 -1
  88. package/kits/docs/evolcore/aid.md +5 -2
  89. package/kits/docs/evolcore/contact.md +57 -0
  90. package/kits/docs/evolcore/fs.md +9 -0
  91. package/kits/docs/evolcore/group.md +12 -3
  92. package/kits/docs/evolcore/model.md +4 -1
  93. package/kits/docs/evolcore/msg.md +9 -3
  94. package/kits/docs/evolcore/response.md +16 -21
  95. package/kits/docs/evolcore/rpc.md +2 -0
  96. package/kits/docs/evolcore/stats.md +15 -2
  97. package/kits/docs/evolcore/storage.md +1 -0
  98. package/kits/docs/evolcore/trigger.md +17 -2
  99. package/kits/eck_manifest.json +12 -0
  100. package/kits/migrations/migrate-contact-book-v2.mjs +747 -0
  101. package/kits/schemas/_meta.json +7 -4
  102. package/kits/schemas/agent-config.schema.6.json +322 -0
  103. package/kits/schemas/contact-book.schema.2.json +43 -0
  104. package/kits/schemas/relation-config.schema.5.json +47 -0
  105. package/kits/schemas/role-config.schema.1.json +1 -0
  106. package/kits/schemas/role-registry.schema.1.json +2 -2
  107. package/kits/templates/roles/admin.json +1 -0
  108. package/kits/templates/roles/member.json +1 -0
  109. package/kits/templates/roles/owner.json +1 -0
  110. package/kits/templates/roles/visitor.json +1 -0
  111. package/kits/templates/system-fragments/commands.md +3 -1
  112. package/package.json +4 -4
  113. package/assets/brand/evolcore/README.md +0 -19
  114. package/assets/brand/evolcore/evolcore-app-icon.png +0 -0
  115. package/assets/brand/evolcore/evolcore-app-icon.svg +0 -13
  116. package/assets/brand/evolcore/evolcore-brand-board.png +0 -0
  117. package/assets/brand/evolcore/evolcore-brand-board.svg +0 -126
  118. package/assets/brand/evolcore/evolcore-logo-kit.zip +0 -0
  119. package/assets/brand/evolcore/evolcore-logo-reverse.png +0 -0
  120. package/assets/brand/evolcore/evolcore-logo-reverse.svg +0 -14
  121. package/assets/brand/evolcore/evolcore-logo.png +0 -0
  122. package/assets/brand/evolcore/evolcore-logo.svg +0 -14
  123. package/assets/brand/evolcore/evolcore-mark.png +0 -0
  124. package/assets/brand/evolcore/evolcore-mark.svg +0 -10
package/dist/cli/init.js CHANGED
@@ -64,27 +64,36 @@ export function parseOwnerAids(raw, isValid) {
64
64
  function installErrorMessage(e) {
65
65
  return String(e?.stderr || e?.message || e).trim();
66
66
  }
67
- async function ensureEcwebInstalledForInit() {
68
- if (commandExists(WEB_CLI_BIN))
69
- return true;
70
- console.log(` 📦 EC Web 组件未安装,正在安装 ${WEB_PACKAGE_LATEST}...`);
67
+ export async function ensureEcwebInstalledForInit(options = {}) {
68
+ const isInstalled = options.isInstalled ?? (() => commandExists(WEB_CLI_BIN));
69
+ if (isInstalled())
70
+ return { ok: true, installedNow: false };
71
+ if (!options.quiet)
72
+ console.log(` 📦 EC Web 组件未安装,正在安装 ${WEB_PACKAGE_LATEST}...`);
71
73
  try {
72
- const { npmInstallGlobal } = await import('../utils/npm-ops.js');
73
- await npmInstallGlobal(WEB_PACKAGE_LATEST);
74
- console.log(' ✓ EC Web 组件安装完成');
75
- return true;
74
+ const install = options.install ?? (async (pkg) => {
75
+ const { npmInstallGlobal } = await import('../utils/npm-ops.js');
76
+ await npmInstallGlobal(pkg);
77
+ });
78
+ await install(WEB_PACKAGE_LATEST);
79
+ if (!options.quiet)
80
+ console.log(' ✓ EC Web 组件安装完成');
81
+ return { ok: true, installedNow: true };
76
82
  }
77
83
  catch (e) {
78
- console.log(` ⚠ EC Web 组件安装失败: ${installErrorMessage(e)}`);
79
- console.log(` 可稍后运行 ec watch web 自动安装并启动,或手动运行 npm install -g ${WEB_PACKAGE_LATEST}`);
80
- return false;
84
+ const error = installErrorMessage(e);
85
+ if (!options.quiet) {
86
+ console.log(` ⚠ EC Web 组件安装失败: ${error}`);
87
+ console.log(` 可稍后运行 ec watch web 自动安装并启动,或手动运行 npm install -g ${WEB_PACKAGE_LATEST}`);
88
+ }
89
+ return { ok: false, error };
81
90
  }
82
91
  }
83
92
  // ==================== Main ====================
84
93
  export async function cmdInit(options) {
85
94
  // 云部署非交互式路径:当带 --owner 或 --format json 时走结构化分支,与交互式 tail 完全隔离。
86
95
  // 预留方案 B:当 daemon 支持 Control Plane 无 agent 启动时,可在此分支跳过 agent new 预创建。
87
- if (options?.nonInteractive && (options.owner || options.format === 'json')) {
96
+ if (options?.nonInteractive && (options.owner || options.format === 'json' || options.projectpath || options.ecweb)) {
88
97
  return cmdInitNonInteractive({
89
98
  owner: options.owner,
90
99
  baseagent: options.baseagent,
@@ -339,16 +348,22 @@ export async function initTail() {
339
348
  if (daemonConfigForEcweb.ecweb?.enabled === undefined) {
340
349
  const ans = (await ask(rl, '\n是否在 ec start 时自动启动 ECWeb 控制台?[y/N] ')).trim().toLowerCase();
341
350
  if (ans === 'y' || ans === 'yes') {
342
- const cfg = loadDaemonConfig();
343
- saveDaemonConfig({ ...cfg, ecweb: { ...(cfg.ecweb ?? {}), enabled: true } });
344
- console.log(' ✓ 已启用 ECWeb(ec start 将自动在后台启动)');
345
- const installed = await ensureEcwebInstalledForInit();
346
- console.log(installed
347
- ? ' 提示:首次访问运行 ec watch web 查看配对码和 URL'
348
- : ' 提示:安装完成后运行 ec start;如需立即打开控制台,运行 ec watch web');
351
+ const installResult = await ensureEcwebInstalledForInit();
352
+ if (installResult.ok) {
353
+ const cfg = loadDaemonConfig();
354
+ saveDaemonConfig({ ...cfg, ecweb: { ...(cfg.ecweb ?? {}), enabled: true } });
355
+ console.log(' ✓ 已启用 ECWeb(ec start 将自动在后台启动)');
356
+ console.log(' 提示:首次访问运行 ec watch web 查看配对码和 URL');
357
+ }
358
+ else {
359
+ const cfg = loadDaemonConfig();
360
+ saveDaemonConfig({ ...cfg, ecweb: { ...(cfg.ecweb ?? {}), enabled: false } });
361
+ console.log(' 提示:安装完成后运行 ec start;如需立即打开控制台,运行 ec watch web');
362
+ }
349
363
  }
350
364
  else {
351
- saveDaemonConfig({ ...loadDaemonConfig(), ecweb: { enabled: false } });
365
+ const cfg = loadDaemonConfig();
366
+ saveDaemonConfig({ ...cfg, ecweb: { ...(cfg.ecweb ?? {}), enabled: false } });
352
367
  console.log(' 已跳过(可日后运行 ec watch web 手动启动,或编辑 daemon.json)');
353
368
  }
354
369
  }
@@ -436,10 +451,13 @@ function fail(code, message, exitCode, format) {
436
451
  emitResult({ type: 'init.result', success: false, error: { code, message } }, format);
437
452
  process.exit(exitCode);
438
453
  }
439
- export async function cmdInitNonInteractive(opts) {
454
+ export async function cmdInitNonInteractive(opts, dependencies = {}) {
440
455
  const format = opts.format;
441
456
  const p = resolvePaths();
442
457
  ensureDataDirs();
458
+ if (format !== undefined && format !== 'json') {
459
+ fail('INVALID_FORMAT', `--format only supports json: ${format}`, EXIT_USAGE, format);
460
+ }
443
461
  // ── 1. owner 校验 ──
444
462
  if (!opts.owner) {
445
463
  fail('MISSING_OWNER', '--owner is required in non-interactive mode', EXIT_USAGE, format);
@@ -459,7 +477,7 @@ export async function cmdInitNonInteractive(opts) {
459
477
  fail('DAEMON_RUNNING', `EvolCore daemon is running (PID: ${pids}); run 'ec stop' first`, EXIT_USAGE, format);
460
478
  }
461
479
  // ── 3. baseagent 探测 + 校验 ──
462
- const available = detectAvailable();
480
+ const available = dependencies.availableBaseagents ?? detectAvailable();
463
481
  if (available.length === 0) {
464
482
  fail('BASEAGENT_UNAVAILABLE', 'no baseagent CLI detected (install claude/codex/gemini)', EXIT_USAGE, format);
465
483
  }
@@ -503,14 +521,33 @@ export async function cmdInitNonInteractive(opts) {
503
521
  }
504
522
  const forced = differentOwner && !!opts.force;
505
523
  const previousOwners = forced ? [...existingOwners] : undefined;
506
- // ── 6. defaults.json ──
524
+ // ── 6. EC Web 安装(先安装成功,再写 enabled 配置)──
525
+ if (opts.ecweb === true) {
526
+ const ensureEcweb = dependencies.ensureEcweb ?? (() => ensureEcwebInstalledForInit({ quiet: true }));
527
+ const installResult = await ensureEcweb();
528
+ if (!installResult.ok) {
529
+ if (existingCfg.ecweb?.enabled === true) {
530
+ try {
531
+ saveDaemonConfig({
532
+ ...existingCfg,
533
+ ecweb: { ...(existingCfg.ecweb ?? {}), enabled: false },
534
+ });
535
+ }
536
+ catch (e) {
537
+ fail('ECWEB_INSTALL_FAILED', `failed to install ${WEB_PACKAGE_LATEST}: ${installResult.error}; failed to disable ecweb: ${e?.message || e}`, EXIT_RUNTIME, format);
538
+ }
539
+ }
540
+ fail('ECWEB_INSTALL_FAILED', `failed to install ${WEB_PACKAGE_LATEST}: ${installResult.error}`, EXIT_RUNTIME, format);
541
+ }
542
+ }
543
+ // ── 7. 写 defaults.json ──
507
544
  try {
508
545
  saveDefaultsSafe(buildDefaults(chosenBaseagent, available, projectsDefaultPath));
509
546
  }
510
547
  catch (e) {
511
548
  fail('IO_ERROR', `failed to write defaults.json: ${e?.message || e}`, EXIT_RUNTIME, format);
512
549
  }
513
- // ── 7. 控制 AID:缺失则生成 ──
550
+ // ── 8. 控制 AID:缺失则生成 ──
514
551
  let controlAid = existingCfg.aid;
515
552
  if (!controlAid) {
516
553
  try {
@@ -521,7 +558,7 @@ export async function cmdInitNonInteractive(opts) {
521
558
  fail('CONTROL_AID_CREATE_FAILED', `gateway unreachable: ${e?.message || e}`, EXIT_RUNTIME, format);
522
559
  }
523
560
  }
524
- // ── 8. 写 daemon.json(aid + owners + ecweb)──
561
+ // ── 9. 写 daemon.json(aid + owners + ecweb)──
525
562
  try {
526
563
  const next = {
527
564
  ...existingCfg,
@@ -535,7 +572,7 @@ export async function cmdInitNonInteractive(opts) {
535
572
  catch (e) {
536
573
  fail('IO_ERROR', `failed to write daemon.json: ${e?.message || e}`, EXIT_RUNTIME, format);
537
574
  }
538
- // ── 9. 输出 init.result ──
575
+ // ── 10. 输出 init.result ──
539
576
  const result = {
540
577
  type: 'init.result',
541
578
  success: true,
package/dist/cli/model.js CHANGED
@@ -243,13 +243,14 @@ Commands:
243
243
  check 诊断网关连通性与模型可用性(分阶段输出进度)
244
244
 
245
245
  作用域(越具体越优先:关系 > 角色 > agent > defaults):
246
- (无参数) 只读 catalog/defaults,不允许写入
246
+ (无参数) 本机 CLI 只读 catalog/defaults,不允许写入
247
247
  --self <aid> agent级 → config.json
248
248
  --self <aid> --role <role> 角色策略预览(只读)
249
249
  --self <aid> --peer <X> 关系级 → relations/<peerKey>/config.json
250
250
 
251
251
  修改 agent/关系作用域后,对应范围所有会话的下一条消息即时生效。
252
252
  角色策略请通过角色策略编辑器修改。
253
+ 托管会话内省略 --self/--peer/--role 时,命令作用于当前会话。
253
254
 
254
255
  Options:
255
256
  --self <aid> 本端 AID
@@ -10,7 +10,7 @@ import { WebSocket } from 'ws';
10
10
  import { aunPath as defaultAunPath } from '../paths.js';
11
11
  import { isHostChinese } from '../utils/locale.js';
12
12
  import { getAidStore, loadClient, SLOT } from '../aun/aid/store.js';
13
- import { isHelpFlag } from './help.js';
13
+ import { isHelpFlag, wantsHelp } from './help.js';
14
14
  const GREEN = '\x1b[32m';
15
15
  const RED = '\x1b[31m';
16
16
  const YELLOW = '\x1b[33m';
@@ -645,7 +645,7 @@ export async function cmdNet(args) {
645
645
  const sub = args[0];
646
646
  const formatJson = args.includes('--format') && args.includes('json');
647
647
  const kickTest = args.includes('--kick-test');
648
- if (isHelpFlag(sub)) {
648
+ if (isHelpFlag(sub) || wantsHelp(args)) {
649
649
  console.log(`用法: ec net check [<aid>] [--format json] [--kick-test]
650
650
 
651
651
  检查 AUN 网络链路连通性(11 步逐层诊断)。
@@ -1,8 +1,8 @@
1
1
  import { resolvePaths } from '../paths.js';
2
2
  import { ipcQuery } from '../ipc.js';
3
- import { isHelpFlag } from './help.js';
3
+ import { getArgValue, isHelpFlag, wantsHelp } from './help.js';
4
4
  export async function cmdQueue(args) {
5
- if (args.length === 0 || isHelpFlag(args[0])) {
5
+ if (args.length === 0 || isHelpFlag(args[0]) || wantsHelp(args)) {
6
6
  console.log(`用法: ec queue --agent <aid> [选项]
7
7
 
8
8
  查询与操作 EvolAgent 的消息队列。
@@ -24,7 +24,7 @@ export async function cmdQueue(args) {
24
24
  ec queue --agent mybot.agentid.pub --clear
25
25
  ec queue --agent mybot.agentid.pub --cancel msg_d4e5f6
26
26
  ec queue --agent mybot.agentid.pub --interrupt --sessionkey feishu#oc_abc123#main`);
27
- process.exit(0);
27
+ return;
28
28
  }
29
29
  // 解析参数
30
30
  const agentIdx = args.indexOf('--agent');
@@ -33,15 +33,39 @@ export async function cmdQueue(args) {
33
33
  process.exit(1);
34
34
  }
35
35
  const agent = args[agentIdx + 1];
36
+ if (agent.startsWith('--')) {
37
+ console.error('❌ --agent 缺少 <aid> 参数');
38
+ process.exit(1);
39
+ }
36
40
  const showId = args.includes('--showid');
37
41
  const full = args.includes('--full');
38
- const formatJson = args.includes('--format json');
42
+ const formatValue = getArgValue(args, '--format');
43
+ if (formatValue !== undefined && formatValue !== 'json') {
44
+ console.error(`❌ --format 仅支持 json: ${formatValue}`);
45
+ process.exit(1);
46
+ }
47
+ const formatJson = formatValue === 'json';
39
48
  const clear = args.includes('--clear');
40
49
  const cancelIdx = args.indexOf('--cancel');
41
- const cancelMsgId = cancelIdx >= 0 && cancelIdx + 1 < args.length ? args[cancelIdx + 1] : undefined;
50
+ const cancelCandidate = cancelIdx >= 0 && cancelIdx + 1 < args.length ? args[cancelIdx + 1] : undefined;
51
+ const cancelMsgId = cancelCandidate && !cancelCandidate.startsWith('--') ? cancelCandidate : undefined;
42
52
  const interrupt = args.includes('--interrupt');
43
53
  const sessionKeyIdx = args.indexOf('--sessionkey');
44
- const sessionKey = sessionKeyIdx >= 0 && sessionKeyIdx + 1 < args.length ? args[sessionKeyIdx + 1] : undefined;
54
+ const sessionCandidate = sessionKeyIdx >= 0 && sessionKeyIdx + 1 < args.length ? args[sessionKeyIdx + 1] : undefined;
55
+ const sessionKey = sessionCandidate && !sessionCandidate.startsWith('--') ? sessionCandidate : undefined;
56
+ const actionCount = [clear, cancelIdx >= 0, interrupt].filter(Boolean).length;
57
+ if (actionCount > 1) {
58
+ console.error('❌ --clear、--cancel、--interrupt 互斥,只能指定一个');
59
+ process.exit(1);
60
+ }
61
+ if (cancelIdx >= 0 && !cancelMsgId) {
62
+ console.error('❌ --cancel 缺少 <messageId>');
63
+ process.exit(1);
64
+ }
65
+ if (interrupt && !sessionKey) {
66
+ console.error('❌ --interrupt 必须配合 --sessionkey <key>');
67
+ process.exit(1);
68
+ }
45
69
  // 构建 IPC 请求
46
70
  const action = clear ? 'clear'
47
71
  : cancelMsgId ? 'cancel'
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Temporarily listen for single-key input and restore stdin's previous state.
3
+ *
4
+ * Standalone init commands enter this flow after closing readline, so stdin is
5
+ * paused and must be paused again during cleanup or Node will stay alive. Some
6
+ * parent wizards keep readline active, so an already-flowing stdin must remain
7
+ * flowing for their next prompt.
8
+ */
9
+ export function setupRawKeyListener(onKey, input = process.stdin) {
10
+ if (!input.isTTY)
11
+ return () => { };
12
+ const wasPaused = input.isPaused();
13
+ const wasRaw = Boolean(input.isRaw);
14
+ input.setRawMode(true);
15
+ input.resume();
16
+ input.setEncoding('utf8');
17
+ const handler = (chunk) => onKey(String(chunk));
18
+ input.on('data', handler);
19
+ return () => {
20
+ input.removeListener('data', handler);
21
+ input.setRawMode(wasRaw);
22
+ if (wasPaused)
23
+ input.pause();
24
+ };
25
+ }
@@ -3,8 +3,7 @@
3
3
  *
4
4
  * 查看/切换/配置会话的响应模式。作用域由 --self/--peer 决定。
5
5
  *
6
- * 三级作用域(越具体越优先:关系 > agent > 全局):
7
- * (无) → 全局 defaults.json
6
+ * 读取解析链(越具体越优先:关系 > agent > defaults):
8
7
  * --self → agent config.json
9
8
  * --self --peer → 关系 relations/<peerKey>/config.json
10
9
  *
@@ -149,11 +148,11 @@ Options:
149
148
  示例:
150
149
  ec response list
151
150
  ec response current --self bot.agentid.pub
152
- ec response info dual-session
151
+ ec response info single-session
153
152
  ec response set single-session --self bot.agentid.pub
154
- ec response set workflow --self bot.agentid.pub --peer aun#team.group.com
155
- ec response config dual-session --self bot.agentid.pub
156
- ec response config set debounceMs 5000 --mode dual-session --self bot.agentid.pub
153
+ ec response set single-session --self bot.agentid.pub --peer aun#team.group.com
154
+ ec response config single-session --self bot.agentid.pub
155
+ ec response config set tool_use_reminder false --mode single-session --self bot.agentid.pub
157
156
  ec response reset --self bot.agentid.pub --peer alice.agentid.pub`;
158
157
  // ── list ────────────────────────────────────────────────────────────────
159
158
  function cmdList(args, formatJson) {
@@ -10,8 +10,10 @@ import { tryUpgrade, tryUpgradeAunSdk, tryUpgradeGlobalPkg, resolveGlobalPkg } f
10
10
  import { resolveAunCoreSdkPkg, AUN_CORE_SDK_PKG } from '../aun/aid/client.js';
11
11
  import { isValidAid } from '../aun/aid/index.js';
12
12
  import { msgSend } from '../aun/msg/index.js';
13
- import { scanInstances, cleanupInstances, writeRestartMonitor, removeRestartMonitor, isRestartMonitorWinner } from '../utils/instance-registry.js';
13
+ import { scanInstances, cleanupInstances, writeRestartMonitor, removeRestartMonitor, isRestartMonitorWinner, findOrphanProcesses, killOrphans } from '../utils/instance-registry.js';
14
14
  import { WEB_PACKAGE_NAME } from '../product.js';
15
+ import { shouldSuppressRealRestart } from '../utils/restart-safety.js';
16
+ import { rotateStdoutLog } from '../utils/log-writer.js';
15
17
  const execFileAsync = promisify(execFile);
16
18
  // 清理 Claude Code 环境变量,防止 SDK 认为是嵌套会话
17
19
  function cleanEnv() {
@@ -24,6 +26,10 @@ function cleanEnv() {
24
26
  }
25
27
  }
26
28
  export async function cmdRestartMonitor() {
29
+ // Defense in depth: a unit test that accidentally executes the real restart
30
+ // path must not detach a persistent daemon from the test worker.
31
+ if (shouldSuppressRealRestart())
32
+ return;
27
33
  const p = resolvePaths();
28
34
  const restartLog = path.join(p.logs, 'restart.log');
29
35
  const MAX_HEAL_ATTEMPTS = 3;
@@ -66,6 +72,17 @@ export async function cmdRestartMonitor() {
66
72
  return s.mains.some(m => m.alive);
67
73
  };
68
74
  log('Restart monitor started');
75
+ // Test daemons live in one temporary HOME per test, so the current HOME's
76
+ // instance registry cannot see them. Their strict environment markers make
77
+ // them safe to reap globally on every real restart.
78
+ {
79
+ const leakedTestDaemons = findOrphanProcesses().filter(o => o.confirmedTestDaemon);
80
+ if (leakedTestDaemons.length > 0) {
81
+ const killed = killOrphans(leakedTestDaemons);
82
+ log(`Cleaned ${killed.length} leaked test daemon(s): ${killed.join(', ')}`);
83
+ await sleep(500);
84
+ }
85
+ }
69
86
  // restart-pending.json 只留给新主进程发送发起会话内的“重启成功”回执。
70
87
  const pendingFile = path.join(p.dataDir, 'restart-pending.json');
71
88
  // 等待所有活 main 进程退出(可能不止一个)
@@ -271,6 +288,13 @@ async function spawnAndWaitReady(p, log, timeout) {
271
288
  // 清理残留 instance 文件和进程
272
289
  cleanupInstances();
273
290
  cleanEnv();
291
+ const stdoutRotation = rotateStdoutLog(p.logs);
292
+ if (stdoutRotation.rotatedPath) {
293
+ log(`Rotated stdout.log -> ${path.basename(stdoutRotation.rotatedPath)}`);
294
+ }
295
+ if (stdoutRotation.removedArchives.length > 0) {
296
+ log(`Removed ${stdoutRotation.removedArchives.length} expired stdout archive(s)`);
297
+ }
274
298
  const stdoutLog = path.join(p.logs, 'stdout.log');
275
299
  const out = fs.openSync(stdoutLog, 'a');
276
300
  const err = fs.openSync(stdoutLog, 'a');
package/dist/cli/stats.js CHANGED
@@ -32,12 +32,14 @@ Usage: ec stats [options]
32
32
  --context <id> 会话 context breakdown 细目
33
33
  --budget 预算状态
34
34
  --top-peers [--limit N] 对端排行
35
+ --top-models [--limit N] 模型排行
36
+ --traffic 消息收发条数与字节数
35
37
  --sql "<query>" 直接执行只读 SQL
36
38
  --rebuild 全量重建日聚合表 usage_daily(运维兜底/排查)
37
- --peers [--limit N] 私聊对端列表(带累计 token/calls/活跃日)
38
- --groups [--limit N] 群聊列表(同上)
39
- --summary 指定时间范围总消耗汇总(token/USD/CNY)
40
- --peer-detail <id> 指定对端 AID 或 peer_key,按天返回消耗明细
39
+ --peers [--limit N] 私聊对端列表(带累计 token/calls/活跃日)
40
+ --groups [--limit N] 群聊列表(同上)
41
+ --summary 指定时间范围总消耗汇总(token/USD/CNY)
42
+ --peer-detail <id> 指定对端 AID 或 peer_key,按天返回消耗明细
41
43
  --task-calls <taskId> 一个 task 的逐次大模型调用明细
42
44
  --session-calls <id> 一个会话的逐次大模型调用明细
43
45
 
@@ -2,7 +2,7 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { resolvePaths } from '../paths.js';
4
4
  import { ipcQuery } from '../ipc.js';
5
- import { isHelpFlag } from './help.js';
5
+ import { isHelpFlag, wantsHelp } from './help.js';
6
6
  import { AGENT_DELEGATION_TOKEN_ENV } from '../core/auth/agent-delegation.js';
7
7
  import { readTaskRuntimeContextFromEnv } from './task-context.js';
8
8
  import { normalizeTriggerDefinition, resolveScriptPath, } from '../trigger/validation.js';
@@ -12,12 +12,25 @@ export async function cmdTrigger(args) {
12
12
  printHelp();
13
13
  return;
14
14
  }
15
- if (sub === 'template') {
16
- handleTemplate(args.slice(1));
15
+ if (wantsHelp(args)) {
16
+ printHelp();
17
17
  return;
18
18
  }
19
19
  const rest = args.slice(1);
20
20
  const json = hasFlag(rest, '--json') || flagValue(rest, '--format') === 'json';
21
+ if (sub === 'template') {
22
+ try {
23
+ handleTemplate(rest);
24
+ }
25
+ catch (err) {
26
+ if (json)
27
+ console.log(JSON.stringify({ ok: false, error: err?.message || String(err) }, null, 2));
28
+ else
29
+ console.error(err?.message || String(err));
30
+ process.exit(1);
31
+ }
32
+ return;
33
+ }
21
34
  try {
22
35
  switch (sub) {
23
36
  case 'list':
@@ -30,6 +43,7 @@ export async function cmdTrigger(args) {
30
43
  await showHistory(rest, json);
31
44
  return;
32
45
  case 'create':
46
+ case 'set':
33
47
  await createTrigger(rest, json);
34
48
  return;
35
49
  case 'update':
@@ -72,11 +86,12 @@ function printHelp() {
72
86
  create --file <trigger.json|trigger-dir> [--enable] [--json]
73
87
  OR
74
88
  create [--agent <aid>] --cron <expr>|--event <pattern> --prompt <text> [--enable] [其他flag]
89
+ set ... (create 的兼容别名)
75
90
  update [--agent <aid>] <triggerId> <参数...> [--if-revision <sha256:...>] [--json]
76
91
  enable [--agent <aid>] <triggerId>
77
92
  disable [--agent <aid>] <triggerId>
78
93
  cancel [--agent <aid>] <triggerId> (disable 的兼容别名)
79
- delete [--agent <aid>] <triggerId>
94
+ delete|remove|rm [--agent <aid>] <triggerId>
80
95
  run [--agent <aid>] <triggerId> [--dry-run] [--json]
81
96
  template list|show <name> [--json]
82
97
 
@@ -85,7 +100,9 @@ Create 参数模式支持:
85
100
  --exec <script|trigger-session|target-session>
86
101
  --prompt <文本>
87
102
  --script-path <路径> --script-runtime <node|python|bash>
88
- --feedback <target|origin|silent> (create 参数模式仅支持 target)
103
+ --script-args <JSON> --script-timeout <时长>
104
+ --feedback <target> (origin/silent 仅可通过 --file 导入)
105
+ --baseagent <name> (仅 --exec trigger-session)
89
106
  --model <模型> --effort <low|medium|high|xhigh|max>
90
107
  --max-runs <次数> --max-duration <时长: 30s|15m|2h|1d>
91
108
  --permission <readonly|auto|request|bypass>(省略则继承当前身份配置)
@@ -95,6 +112,9 @@ Create 参数模式支持:
95
112
  --trigger-thread <per-run|by-trigger>
96
113
  --name <名称>
97
114
 
115
+ 通用输出参数:
116
+ --json | --format json
117
+
98
118
  Update 仅支持常用字段:
99
119
  --once | --delay | --at | --cron | --every | --event [--tz]
100
120
  --prompt <文本> --name <名称>
@@ -120,7 +140,7 @@ async function listTriggers(args, json) {
120
140
  }
121
141
  async function showTrigger(args, json) {
122
142
  const agentAid = resolveAgentAid(args);
123
- const triggerId = positional(args, 0, ['--agent']);
143
+ const triggerId = positional(args, 0, ['--agent', '--format']);
124
144
  const res = await request({ type: 'trigger.show', agentAid, triggerId });
125
145
  if (json)
126
146
  return printJson(res);
@@ -149,6 +169,8 @@ async function showTrigger(args, json) {
149
169
  console.log(`limit state: ${parts.join(', ')}`);
150
170
  }
151
171
  console.log(`execution: ${t.execution.type}`);
172
+ if (t.execution.type === 'trigger_session')
173
+ console.log(`baseagent: ${t.execution.baseagent ?? 'legacy/inherit'}`);
152
174
  if (t.execution.type === 'script')
153
175
  console.log(`script: ${scriptCommandLabel(t.execution.script)}`);
154
176
  console.log(`model: ${t.execution.model ?? 'inherit'}`);
@@ -208,9 +230,8 @@ async function createTrigger(args, json) {
208
230
  return;
209
231
  }
210
232
  // Flag mode: use parser
211
- const { parseTriggerSet } = await import('../trigger/parser.js');
212
- const flagStr = args.filter(a => a !== '--enable' && a !== '--json' && !a.startsWith('--format')).join(' ');
213
- const parseResult = parseTriggerSet(flagStr);
233
+ const { parseTriggerSetArgv } = await import('../trigger/parser.js');
234
+ const parseResult = parseTriggerSetArgv(triggerCreateArgs(args));
214
235
  if (!parseResult.ok) {
215
236
  throw new Error(parseResult.error);
216
237
  }
@@ -246,7 +267,10 @@ async function createTrigger(args, json) {
246
267
  ...(parsed.model ? { model: parsed.model } : {}),
247
268
  ...(parsed.effort ? { effort: parsed.effort } : {}),
248
269
  ...(parsed.permissionMode ? { permissionMode: parsed.permissionMode } : {}),
249
- ...(parsed.executionType === 'trigger_session' ? { thread: parsed.triggerThread ?? 'by_trigger' } : {}),
270
+ ...(parsed.executionType === 'trigger_session' ? {
271
+ thread: parsed.triggerThread ?? 'by_trigger',
272
+ ...(parsed.baseagent ? { baseagent: parsed.baseagent } : {}),
273
+ } : {}),
250
274
  onError: 'retry',
251
275
  noopSentinel: '[[NOOP]]',
252
276
  },
@@ -272,6 +296,22 @@ async function createTrigger(args, json) {
272
296
  return printJson(res);
273
297
  console.log(`✓ created ${res.trigger.id} (${res.trigger.name})`);
274
298
  }
299
+ function triggerCreateArgs(args) {
300
+ const out = [];
301
+ for (let i = 0; i < args.length; i++) {
302
+ const arg = args[i];
303
+ if (arg === '--enable' || arg === '--json')
304
+ continue;
305
+ if (arg === '--format') {
306
+ i += 1;
307
+ continue;
308
+ }
309
+ if (arg.startsWith('--format='))
310
+ continue;
311
+ out.push(arg);
312
+ }
313
+ return out;
314
+ }
275
315
  function sourceFromParsed(parsed) {
276
316
  if (parsed.scheduleType === 'once')
277
317
  return { type: 'once' };
@@ -362,7 +402,7 @@ function triggerUpdateArgs(args) {
362
402
  }
363
403
  async function setEnabled(args, enabled, json) {
364
404
  const agentAid = resolveAgentAid(args);
365
- const triggerId = positional(args, 0, ['--agent']);
405
+ const triggerId = positional(args, 0, ['--agent', '--format']);
366
406
  const res = await request({ type: 'trigger.setEnabled', agentAid, triggerId, enabled });
367
407
  if (json)
368
408
  return printJson(res);
@@ -370,7 +410,7 @@ async function setEnabled(args, enabled, json) {
370
410
  }
371
411
  async function cancelTrigger(args, json) {
372
412
  const agentAid = resolveAgentAid(args);
373
- const triggerId = positional(args, 0, ['--agent']);
413
+ const triggerId = positional(args, 0, ['--agent', '--format']);
374
414
  const res = await request({ type: 'trigger.cancel', agentAid, triggerId });
375
415
  if (json)
376
416
  return printJson(res);
@@ -378,7 +418,7 @@ async function cancelTrigger(args, json) {
378
418
  }
379
419
  async function deleteTrigger(args, json) {
380
420
  const agentAid = resolveAgentAid(args);
381
- const triggerId = positional(args, 0, ['--agent']);
421
+ const triggerId = positional(args, 0, ['--agent', '--format']);
382
422
  const res = await request({ type: 'trigger.delete', agentAid, triggerId });
383
423
  if (json)
384
424
  return printJson(res);
@@ -386,7 +426,7 @@ async function deleteTrigger(args, json) {
386
426
  }
387
427
  async function runTrigger(args, json) {
388
428
  const agentAid = resolveAgentAid(args);
389
- const triggerId = positional(args, 0, ['--agent']);
429
+ const triggerId = positional(args, 0, ['--agent', '--format']);
390
430
  const res = await request({ type: 'trigger.run', agentAid, triggerId, dryRun: hasFlag(args, '--dry-run') }, 120_000);
391
431
  if (json)
392
432
  return printJson(res);
@@ -406,7 +446,7 @@ function handleTemplate(args) {
406
446
  return;
407
447
  }
408
448
  if (sub === 'show') {
409
- const name = args.find(a => !a.startsWith('-') && a !== 'show');
449
+ const name = optionalPositional(args.slice(1), 0, ['--format']);
410
450
  if (!name)
411
451
  throw new Error('missing template name');
412
452
  if (json)
@@ -5,6 +5,7 @@ import fs from 'fs';
5
5
  import path from 'path';
6
6
  import { fileURLToPath } from 'url';
7
7
  import { MAIN_PACKAGE_NAME, PRODUCT_DISPLAY_NAME, WEB_PACKAGE_NAME } from '../product.js';
8
+ import { getArgValue, wantsHelp } from './help.js';
8
9
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
10
  const PACKAGE_ROOT = path.resolve(__dirname, '../..');
10
11
  const RESET = '\x1b[0m';
@@ -44,7 +45,11 @@ function findInstalledVersion(pkgName) {
44
45
  return { name: pkg.name || pkgName, version: pkg.version || '?', buildTs: ts || undefined, path: dir };
45
46
  }
46
47
  export function handleVersion(args) {
47
- const isJson = args.includes('--format') && args.includes('json') || args.includes('--json');
48
+ if (wantsHelp(args)) {
49
+ console.log(`用法: ec version [--format json|--json]\n\n显示 EvolCore、EC Web、AUN SDK、WebSocket 与 Node.js 版本。`);
50
+ return;
51
+ }
52
+ const isJson = getArgValue(args, '--format') === 'json' || args.includes('--json');
48
53
  // 主包
49
54
  const mainPkg = readPkgJson(PACKAGE_ROOT);
50
55
  const mainEntry = path.join(PACKAGE_ROOT, 'dist', 'cli', 'index.js');
@@ -2,7 +2,28 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { kitsRoleTemplatesDir } from '../paths.js';
4
4
  import { validateRoleConfig } from './role-schema.js';
5
+ import { expectedRoleRank } from './role-ranks.js';
5
6
  export const BUILTIN_ROLE_IDS = ['owner', 'admin', 'member', 'visitor'];
7
+ export function validateBuiltinRoleTemplateDefinition(roleId, raw) {
8
+ const issues = validateRoleConfig(raw);
9
+ if (issues.length) {
10
+ return {
11
+ definition: null,
12
+ error: `built-in role template failed validation: ${issues.map(issue => `${issue.field} ${issue.message}`).join('; ')}`,
13
+ };
14
+ }
15
+ const { $schema_version: _schemaVersion, ...definition } = raw;
16
+ if (BUILTIN_ROLE_IDS.includes(roleId)) {
17
+ const expectedRank = expectedRoleRank(roleId);
18
+ if (definition.rank !== expectedRank) {
19
+ return {
20
+ definition: null,
21
+ error: `built-in role template rank must be ${expectedRank}`,
22
+ };
23
+ }
24
+ }
25
+ return { definition: definition };
26
+ }
6
27
  /** Load the versioned package template that defines a built-in role policy. */
7
28
  export function readBuiltinRoleTemplate(roleId) {
8
29
  const file = path.join(kitsRoleTemplatesDir(), `${roleId}.json`);
@@ -17,14 +38,5 @@ export function readBuiltinRoleTemplate(roleId) {
17
38
  error: `cannot read built-in role template: ${error instanceof Error ? error.message : String(error)}`,
18
39
  };
19
40
  }
20
- const issues = validateRoleConfig(raw);
21
- if (issues.length) {
22
- return {
23
- definition: null,
24
- file,
25
- error: `built-in role template failed validation: ${issues.map(issue => `${issue.field} ${issue.message}`).join('; ')}`,
26
- };
27
- }
28
- const { $schema_version: _schemaVersion, ...definition } = raw;
29
- return { definition: definition, file };
41
+ return { ...validateBuiltinRoleTemplateDefinition(roleId, raw), file };
30
42
  }