evolcore 0.0.2 → 0.0.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 (206) hide show
  1. package/CHANGELOG.md +93 -778
  2. package/README.md +45 -10
  3. package/bin/ec-safe-output.js +89 -24
  4. package/dist/agents/baseagent.js +46 -0
  5. package/dist/agents/claude-runner.js +198 -48
  6. package/dist/agents/codex-app-server-client.js +68 -0
  7. package/dist/agents/codex-runner.js +203 -8
  8. package/dist/agents/runner-types.js +2 -2
  9. package/dist/aun/aid/agentmd.js +79 -0
  10. package/dist/aun/aid/encryption-seed-policy.js +29 -0
  11. package/dist/aun/aid/index.js +1 -1
  12. package/dist/aun/aid/store.js +2 -5
  13. package/dist/aun/outbox.js +8 -0
  14. package/dist/channels/aun.js +241 -116
  15. package/dist/channels/daemon.js +18 -4
  16. package/dist/channels/dingtalk.js +1005 -260
  17. package/dist/channels/feishu.js +184 -56
  18. package/dist/channels/qqbot.js +23 -1
  19. package/dist/channels/wechat.js +303 -155
  20. package/dist/channels/wecom.js +1132 -164
  21. package/dist/cli/agent-command.js +2 -1
  22. package/dist/cli/aun-commands.js +98 -43
  23. package/dist/cli/bench.js +2 -2
  24. package/dist/cli/contact.js +71 -0
  25. package/dist/cli/ctl-command.js +2 -2
  26. package/dist/cli/daemon-commands.js +77 -214
  27. package/dist/cli/handoff-command.js +4 -3
  28. package/dist/cli/help.js +9 -5
  29. package/dist/cli/index.js +132 -116
  30. package/dist/cli/init-channel.js +242 -129
  31. package/dist/cli/init.js +70 -31
  32. package/dist/cli/model.js +2 -1
  33. package/dist/cli/net-check.js +2 -2
  34. package/dist/cli/queue-command.js +30 -6
  35. package/dist/cli/raw-key-input.js +25 -0
  36. package/dist/cli/response.js +5 -6
  37. package/dist/cli/restart-monitor.js +25 -1
  38. package/dist/cli/stats.js +6 -4
  39. package/dist/cli/trigger-command.js +146 -25
  40. package/dist/cli/version.js +6 -1
  41. package/dist/config/access-policy-domain.js +38 -0
  42. package/dist/config/access-policy.js +134 -0
  43. package/dist/config/builtin-role-templates.js +42 -17
  44. package/dist/config/builtin-roles.js +31 -6
  45. package/dist/config/config-field-policy.js +17 -5
  46. package/dist/config/config-manager.js +409 -29
  47. package/dist/config/config-operation-service.js +35 -38
  48. package/dist/config/contact-alias.js +68 -0
  49. package/dist/config/contact-bind-code.js +274 -0
  50. package/dist/config/contact-book-store.js +622 -0
  51. package/dist/config/contact-book-v2-startup.js +35 -0
  52. package/dist/config/contact-book.js +185 -300
  53. package/dist/config/contact-operation-service.js +116 -0
  54. package/dist/config/contact-request-service.js +331 -0
  55. package/dist/config/peer-role-resolver.js +136 -55
  56. package/dist/config/role-ranks.js +18 -0
  57. package/dist/config/role-service.js +16 -19
  58. package/dist/config/role-store.js +16 -5
  59. package/dist/config/roles.js +10 -1
  60. package/dist/config/schema-registry.js +49 -24
  61. package/dist/config-store.js +14 -4
  62. package/dist/core/auth/agent-delegation.js +105 -11
  63. package/dist/core/auth/authorization-audit.js +16 -1
  64. package/dist/core/auth/operation-authorizer.js +10 -0
  65. package/dist/core/auth/operation-catalog.js +105 -1
  66. package/dist/core/auth/trigger-authorization.js +15 -0
  67. package/dist/core/bootstrap-service.js +2 -9
  68. package/dist/core/channel-loader.js +6 -2
  69. package/dist/core/command/command-handler.js +113 -21
  70. package/dist/core/command/connect-menu.js +599 -0
  71. package/dist/core/command/evol-menu-version-gate.js +38 -0
  72. package/dist/core/command/group-menu.js +421 -0
  73. package/dist/core/command/menu-handler.js +382 -100
  74. package/dist/core/command/menu-protocol.js +8 -2
  75. package/dist/core/command/menu-token-store.js +102 -0
  76. package/dist/core/command/role-menu.js +128 -29
  77. package/dist/core/command/slash-gate.js +1 -1
  78. package/dist/core/command/slash-handler.js +186 -49
  79. package/dist/core/daemon-file-cache.js +40 -6
  80. package/dist/core/event-catalog.js +99 -0
  81. package/dist/core/evolagent-registry.js +4 -40
  82. package/dist/core/evolagent.js +20 -9
  83. package/dist/core/handoff/runtime.js +162 -37
  84. package/dist/core/handoff/store.js +46 -2
  85. package/dist/core/handoff/types.js +1 -0
  86. package/dist/core/inference/text-inference.js +16 -74
  87. package/dist/core/message/file-markers.js +7 -0
  88. package/dist/core/message/im-renderer.js +20 -14
  89. package/dist/core/message/inbound-admission.js +124 -0
  90. package/dist/core/message/message-bridge.js +670 -57
  91. package/dist/core/message/message-log.js +1 -0
  92. package/dist/core/message/message-queue.js +195 -10
  93. package/dist/core/message/message-utils.js +8 -2
  94. package/dist/core/message/peer-mode.js +7 -8
  95. package/dist/core/message/response-engine.js +642 -96
  96. package/dist/core/message/send-receipt.js +24 -0
  97. package/dist/core/message/stream-debouncer.js +11 -2
  98. package/dist/core/permission/approval-gateway.js +17 -10
  99. package/dist/core/permission/ec-command-parser.js +101 -46
  100. package/dist/core/permission/tool-policy.js +115 -38
  101. package/dist/core/protected-paths.js +38 -11
  102. package/dist/core/session/session-fs-store.js +44 -3
  103. package/dist/core/session/session-manager.js +86 -8
  104. package/dist/core/session/session-mapper.js +2 -0
  105. package/dist/core/session/session-renew.js +125 -69
  106. package/dist/core/session/session-turn-coordinator.js +5 -1
  107. package/dist/eck/kit-renderer.js +12 -1
  108. package/dist/eck/message-renderer.js +79 -1
  109. package/dist/index.js +285 -94
  110. package/dist/ipc.js +114 -8
  111. package/dist/paths.js +3 -0
  112. package/dist/response-system/config-resolver.js +29 -0
  113. package/dist/response-system/coordinator.js +8 -32
  114. package/dist/response-system/engines/v1/proactive-flow.js +1 -1
  115. package/dist/response-system/index.js +1 -0
  116. package/dist/response-system/modes/single-session/index.js +7 -4
  117. package/dist/stats/billing.js +20 -8
  118. package/dist/trigger/manager.js +3 -0
  119. package/dist/trigger/parser.js +126 -11
  120. package/dist/trigger/patch.js +12 -2
  121. package/dist/trigger/scheduler.js +444 -40
  122. package/dist/trigger/validation.js +13 -0
  123. package/dist/utils/aid-bind.js +43 -29
  124. package/dist/utils/error-dict.json +7 -0
  125. package/dist/utils/evolcore-version.js +21 -0
  126. package/dist/utils/instance-registry.js +14 -7
  127. package/dist/utils/log-writer.js +46 -0
  128. package/dist/utils/logger.js +2 -2
  129. package/dist/utils/media-cache.js +4 -1
  130. package/dist/utils/model-prices.jsonl +6 -3
  131. package/dist/utils/restart-safety.js +31 -0
  132. package/dist/utils/stable-semver.js +21 -0
  133. package/dist/utils/stats.js +33 -9
  134. package/dist/utils/system-memory.js +62 -0
  135. package/kits/docs/INDEX.md +2 -1
  136. package/kits/docs/channels/aun.md +4 -13
  137. package/kits/docs/evolcore/INDEX.md +5 -3
  138. package/kits/docs/evolcore/agent.md +9 -1
  139. package/kits/docs/evolcore/aid.md +5 -2
  140. package/kits/docs/evolcore/config.md +47 -2
  141. package/kits/docs/evolcore/contact.md +61 -0
  142. package/kits/docs/evolcore/fs.md +9 -0
  143. package/kits/docs/evolcore/group-rules.md +46 -4
  144. package/kits/docs/evolcore/group.md +15 -6
  145. package/kits/docs/evolcore/model.md +4 -1
  146. package/kits/docs/evolcore/msg.md +12 -6
  147. package/kits/docs/evolcore/response.md +16 -21
  148. package/kits/docs/evolcore/rpc.md +2 -0
  149. package/kits/docs/evolcore/stats.md +15 -2
  150. package/kits/docs/evolcore/storage.md +1 -0
  151. package/kits/docs/evolcore/trigger.md +42 -10
  152. package/kits/docs/path-registry.md +36 -17
  153. package/kits/eck_manifest.json +12 -0
  154. package/kits/eck_message_manifest.json +12 -1
  155. package/kits/migrations/migrate-contact-book-v2.mjs +754 -0
  156. package/kits/rules/01-overview.md +17 -7
  157. package/kits/rules/02-navigation.md +38 -18
  158. package/kits/rules/03-identity.md +28 -24
  159. package/kits/rules/04-relation.md +44 -28
  160. package/kits/rules/05-venue.md +31 -15
  161. package/kits/rules/06-channel.md +11 -7
  162. package/kits/schemas/_meta.json +21 -7
  163. package/kits/schemas/agent-config.schema.6.json +322 -0
  164. package/kits/schemas/agent-config.schema.7.json +303 -0
  165. package/kits/schemas/agent-config.schema.8.json +304 -0
  166. package/kits/schemas/agent-config.schema.9.json +374 -0
  167. package/kits/schemas/contact-book.schema.2.json +43 -0
  168. package/kits/schemas/contact-book.schema.3.json +67 -0
  169. package/kits/schemas/daemon.schema.1.json +3 -2
  170. package/kits/schemas/daemon.schema.2.json +101 -0
  171. package/kits/schemas/daemon.schema.3.json +123 -0
  172. package/kits/schemas/defaults.schema.2.json +85 -0
  173. package/kits/schemas/defaults.schema.3.json +73 -0
  174. package/kits/schemas/relation-config.schema.5.json +47 -0
  175. package/kits/schemas/relation-config.schema.6.json +46 -0
  176. package/kits/schemas/relation-config.schema.7.json +59 -0
  177. package/kits/schemas/role-config.schema.1.json +1 -0
  178. package/kits/schemas/role-registry.schema.1.json +2 -2
  179. package/kits/schemas/single-session.schema.2.json +57 -0
  180. package/kits/templates/message-fragments/handoff-context-to-target.md +9 -0
  181. package/kits/templates/message-fragments/handoff-request-to-target.md +14 -8
  182. package/kits/templates/message-fragments/handoff-response-to-origin.md +5 -6
  183. package/kits/templates/roles/admin.json +47 -0
  184. package/kits/templates/roles/member.json +5 -0
  185. package/kits/templates/roles/owner.json +1 -0
  186. package/kits/templates/roles/visitor.json +5 -0
  187. package/kits/templates/system-fragments/channel.md +12 -2
  188. package/kits/templates/system-fragments/commands.md +3 -1
  189. package/kits/templates/system-fragments/identity.md +3 -1
  190. package/kits/templates/system-fragments/relation.md +1 -1
  191. package/kits/templates/system-fragments/session.md +6 -2
  192. package/package.json +7 -5
  193. package/MIGRATION-0.5.0.md +0 -378
  194. package/ROLE_ACCESS_CONTROL.md +0 -174
  195. package/assets/brand/evolcore/README.md +0 -19
  196. package/assets/brand/evolcore/evolcore-app-icon.png +0 -0
  197. package/assets/brand/evolcore/evolcore-app-icon.svg +0 -13
  198. package/assets/brand/evolcore/evolcore-brand-board.png +0 -0
  199. package/assets/brand/evolcore/evolcore-brand-board.svg +0 -126
  200. package/assets/brand/evolcore/evolcore-logo-kit.zip +0 -0
  201. package/assets/brand/evolcore/evolcore-logo-reverse.png +0 -0
  202. package/assets/brand/evolcore/evolcore-logo-reverse.svg +0 -14
  203. package/assets/brand/evolcore/evolcore-logo.png +0 -0
  204. package/assets/brand/evolcore/evolcore-logo.svg +0 -14
  205. package/assets/brand/evolcore/evolcore-mark.png +0 -0
  206. 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
  }
@@ -375,7 +390,7 @@ export async function initTail() {
375
390
  * Present instance selection menu when existing instances are found.
376
391
  * Returns the user's choice, or null if cancelled.
377
392
  */
378
- export async function selectInstance(rl, channelType, instances) {
393
+ export async function selectInstance(rl, channelType, instances, options = {}) {
379
394
  const typeLabel = channelType === 'feishu' ? '飞书' : channelType === 'wechat' ? '微信' : channelType === 'dingtalk' ? '钉钉' : channelType === 'qqbot' ? 'QQ机器人' : channelType === 'wecom' ? '企业微信' : channelType.toUpperCase();
380
395
  console.log(`\n发现已有 ${typeLabel} 配置:`);
381
396
  const letters = 'abcdefghijklmnopqrstuvwxyz';
@@ -412,10 +427,12 @@ export async function selectInstance(rl, channelType, instances) {
412
427
  }
413
428
  const target = instances[choiceIndex];
414
429
  console.log(`\n已选择:${target.name}`);
415
- const confirm = (await new Promise(r => rl.question(`⚠️ 即将覆盖该配置,确认?(y/N) `, r))).trim().toLowerCase();
416
- if (confirm !== 'y' && confirm !== 'yes') {
417
- console.log('已取消');
418
- return null;
430
+ if (options.confirmOverwrite !== false) {
431
+ const confirm = (await new Promise(r => rl.question(`⚠️ 即将覆盖该配置,确认?(y/N) `, r))).trim().toLowerCase();
432
+ if (confirm !== 'y' && confirm !== 'yes') {
433
+ console.log('已取消');
434
+ return null;
435
+ }
419
436
  }
420
437
  return { action: 'overwrite', index: choiceIndex, name: target.name };
421
438
  }
@@ -436,10 +453,13 @@ function fail(code, message, exitCode, format) {
436
453
  emitResult({ type: 'init.result', success: false, error: { code, message } }, format);
437
454
  process.exit(exitCode);
438
455
  }
439
- export async function cmdInitNonInteractive(opts) {
456
+ export async function cmdInitNonInteractive(opts, dependencies = {}) {
440
457
  const format = opts.format;
441
458
  const p = resolvePaths();
442
459
  ensureDataDirs();
460
+ if (format !== undefined && format !== 'json') {
461
+ fail('INVALID_FORMAT', `--format only supports json: ${format}`, EXIT_USAGE, format);
462
+ }
443
463
  // ── 1. owner 校验 ──
444
464
  if (!opts.owner) {
445
465
  fail('MISSING_OWNER', '--owner is required in non-interactive mode', EXIT_USAGE, format);
@@ -459,7 +479,7 @@ export async function cmdInitNonInteractive(opts) {
459
479
  fail('DAEMON_RUNNING', `EvolCore daemon is running (PID: ${pids}); run 'ec stop' first`, EXIT_USAGE, format);
460
480
  }
461
481
  // ── 3. baseagent 探测 + 校验 ──
462
- const available = detectAvailable();
482
+ const available = dependencies.availableBaseagents ?? detectAvailable();
463
483
  if (available.length === 0) {
464
484
  fail('BASEAGENT_UNAVAILABLE', 'no baseagent CLI detected (install claude/codex/gemini)', EXIT_USAGE, format);
465
485
  }
@@ -503,14 +523,33 @@ export async function cmdInitNonInteractive(opts) {
503
523
  }
504
524
  const forced = differentOwner && !!opts.force;
505
525
  const previousOwners = forced ? [...existingOwners] : undefined;
506
- // ── 6. defaults.json ──
526
+ // ── 6. EC Web 安装(先安装成功,再写 enabled 配置)──
527
+ if (opts.ecweb === true) {
528
+ const ensureEcweb = dependencies.ensureEcweb ?? (() => ensureEcwebInstalledForInit({ quiet: true }));
529
+ const installResult = await ensureEcweb();
530
+ if (!installResult.ok) {
531
+ if (existingCfg.ecweb?.enabled === true) {
532
+ try {
533
+ saveDaemonConfig({
534
+ ...existingCfg,
535
+ ecweb: { ...(existingCfg.ecweb ?? {}), enabled: false },
536
+ });
537
+ }
538
+ catch (e) {
539
+ fail('ECWEB_INSTALL_FAILED', `failed to install ${WEB_PACKAGE_LATEST}: ${installResult.error}; failed to disable ecweb: ${e?.message || e}`, EXIT_RUNTIME, format);
540
+ }
541
+ }
542
+ fail('ECWEB_INSTALL_FAILED', `failed to install ${WEB_PACKAGE_LATEST}: ${installResult.error}`, EXIT_RUNTIME, format);
543
+ }
544
+ }
545
+ // ── 7. 写 defaults.json ──
507
546
  try {
508
547
  saveDefaultsSafe(buildDefaults(chosenBaseagent, available, projectsDefaultPath));
509
548
  }
510
549
  catch (e) {
511
550
  fail('IO_ERROR', `failed to write defaults.json: ${e?.message || e}`, EXIT_RUNTIME, format);
512
551
  }
513
- // ── 7. 控制 AID:缺失则生成 ──
552
+ // ── 8. 控制 AID:缺失则生成 ──
514
553
  let controlAid = existingCfg.aid;
515
554
  if (!controlAid) {
516
555
  try {
@@ -521,7 +560,7 @@ export async function cmdInitNonInteractive(opts) {
521
560
  fail('CONTROL_AID_CREATE_FAILED', `gateway unreachable: ${e?.message || e}`, EXIT_RUNTIME, format);
522
561
  }
523
562
  }
524
- // ── 8. 写 daemon.json(aid + owners + ecweb)──
563
+ // ── 9. 写 daemon.json(aid + owners + ecweb)──
525
564
  try {
526
565
  const next = {
527
566
  ...existingCfg,
@@ -535,7 +574,7 @@ export async function cmdInitNonInteractive(opts) {
535
574
  catch (e) {
536
575
  fail('IO_ERROR', `failed to write daemon.json: ${e?.message || e}`, EXIT_RUNTIME, format);
537
576
  }
538
- // ── 9. 输出 init.result ──
577
+ // ── 10. 输出 init.result ──
539
578
  const result = {
540
579
  type: 'init.result',
541
580
  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