evolcore 0.0.7 → 0.0.9

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 (230) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +38 -4
  3. package/dist/agents/baseagent.js +117 -24
  4. package/dist/agents/claude-runner.js +31 -2
  5. package/dist/agents/codex-app-server-client.js +37 -2
  6. package/dist/agents/codex-runner.js +11 -3
  7. package/dist/aun/aid/control-aid.js +40 -27
  8. package/dist/aun/aid/domain.js +23 -0
  9. package/dist/aun/msg/group.js +9 -9
  10. package/dist/aun/msg/p2p.js +11 -6
  11. package/dist/aun/msg/upload.js +43 -29
  12. package/dist/aun/outbox.js +48 -5
  13. package/dist/channels/aun.js +225 -71
  14. package/dist/cli/aun-commands.js +10 -7
  15. package/dist/cli/bench.js +4 -3
  16. package/dist/cli/daemon-commands.js +208 -68
  17. package/dist/cli/data-command.js +62 -35
  18. package/dist/cli/index.js +11 -1
  19. package/dist/cli/init-channel.js +1 -1
  20. package/dist/cli/init.js +112 -17
  21. package/dist/cli/model.js +5 -5
  22. package/dist/cli/restart-monitor.js +102 -22
  23. package/dist/config/builtin-role-templates.js +4 -3
  24. package/dist/config/config-field-policy.js +6 -2
  25. package/dist/config/config-manager.js +79 -11
  26. package/dist/config/gateway-config.js +80 -35
  27. package/dist/config/role-schema.js +18 -3
  28. package/dist/config/role-service.js +3 -3
  29. package/dist/config/role-store.js +4 -3
  30. package/dist/config-store.js +19 -3
  31. package/dist/core/auth/operation-authorizer.js +3 -1
  32. package/dist/core/baseagent-loader.js +5 -3
  33. package/dist/core/bootstrap-service.js +0 -12
  34. package/dist/core/capability/providers/codex-capability-provider.js +2 -2
  35. package/dist/core/channel-loader.js +15 -2
  36. package/dist/core/command/command-handler.js +1 -1
  37. package/dist/core/command/evol-menu-version-gate.js +3 -2
  38. package/dist/core/command/menu-handler.js +50 -28
  39. package/dist/core/command/menu-protocol.js +7 -4
  40. package/dist/core/command/slash-handler.js +4 -4
  41. package/dist/core/data-migration.js +517 -24
  42. package/dist/core/inference/text-inference.js +18 -5
  43. package/dist/core/message/message-bridge.js +17 -6
  44. package/dist/core/message/response-engine.js +22 -3
  45. package/dist/core/model/config-scope.js +3 -3
  46. package/dist/core/permission/tool-policy.js +18 -0
  47. package/dist/core/protected-paths.js +12 -1
  48. package/dist/index.js +794 -725
  49. package/dist/ipc.js +3 -1
  50. package/dist/product.js +1 -0
  51. package/dist/utils/autostart.js +27 -0
  52. package/dist/utils/codex-cli.js +39 -0
  53. package/dist/utils/cross-platform.js +147 -18
  54. package/dist/utils/ecweb-utils.js +15 -2
  55. package/dist/utils/instance-registry.js +43 -6
  56. package/dist/utils/macos-autostart.js +179 -0
  57. package/dist/utils/process-introspect.js +7 -3
  58. package/dist/utils/windows-autostart.js +130 -0
  59. package/ecagent/dist/agent-loop.d.ts +1 -1
  60. package/ecagent/dist/agent-loop.js +1 -1
  61. package/ecagent/dist/agent.d.ts +1 -1
  62. package/ecagent/dist/agent.js +1 -1
  63. package/ecagent/dist/harness/agent-harness.d.ts +1 -1
  64. package/ecagent/dist/harness/compaction/branch-summarization.d.ts +1 -1
  65. package/ecagent/dist/harness/compaction/compaction.d.ts +1 -1
  66. package/ecagent/dist/harness/compaction/utils.d.ts +1 -1
  67. package/ecagent/dist/harness/messages.d.ts +1 -1
  68. package/ecagent/dist/harness/session/session.d.ts +1 -1
  69. package/ecagent/dist/harness/types.d.ts +1 -1
  70. package/ecagent/dist/proxy.d.ts +1 -1
  71. package/ecagent/dist/proxy.js +1 -1
  72. package/ecagent/dist/types.d.ts +1 -1
  73. package/kits/migrations/migrate-role-config-v5.mjs +27 -2
  74. package/kits/rules/01-overview.md +3 -2
  75. package/kits/schemas/_meta.json +2 -1
  76. package/kits/schemas/agent-config.schema.10.json +19 -1
  77. package/kits/schemas/daemon.schema.4.json +136 -0
  78. package/kits/schemas/defaults.schema.4.json +15 -1
  79. package/kits/schemas/relation-config.schema.8.json +22 -1
  80. package/kits/schemas/role-config.schema.1.json +2 -2
  81. package/package.json +2 -4
  82. package/ecagent/dist/agent-loop.d.ts.map +0 -1
  83. package/ecagent/dist/agent-loop.js.map +0 -1
  84. package/ecagent/dist/agent.d.ts.map +0 -1
  85. package/ecagent/dist/agent.js.map +0 -1
  86. package/ecagent/dist/harness/agent-harness.d.ts.map +0 -1
  87. package/ecagent/dist/harness/agent-harness.js.map +0 -1
  88. package/ecagent/dist/harness/compaction/branch-summarization.d.ts.map +0 -1
  89. package/ecagent/dist/harness/compaction/branch-summarization.js.map +0 -1
  90. package/ecagent/dist/harness/compaction/compaction.d.ts.map +0 -1
  91. package/ecagent/dist/harness/compaction/compaction.js.map +0 -1
  92. package/ecagent/dist/harness/compaction/utils.d.ts.map +0 -1
  93. package/ecagent/dist/harness/compaction/utils.js.map +0 -1
  94. package/ecagent/dist/harness/env/nodejs.d.ts.map +0 -1
  95. package/ecagent/dist/harness/env/nodejs.js.map +0 -1
  96. package/ecagent/dist/harness/messages.d.ts.map +0 -1
  97. package/ecagent/dist/harness/messages.js.map +0 -1
  98. package/ecagent/dist/harness/prompt-templates.d.ts.map +0 -1
  99. package/ecagent/dist/harness/prompt-templates.js.map +0 -1
  100. package/ecagent/dist/harness/session/jsonl-repo.d.ts.map +0 -1
  101. package/ecagent/dist/harness/session/jsonl-repo.js.map +0 -1
  102. package/ecagent/dist/harness/session/jsonl-storage.d.ts.map +0 -1
  103. package/ecagent/dist/harness/session/jsonl-storage.js.map +0 -1
  104. package/ecagent/dist/harness/session/memory-repo.d.ts.map +0 -1
  105. package/ecagent/dist/harness/session/memory-repo.js.map +0 -1
  106. package/ecagent/dist/harness/session/memory-storage.d.ts.map +0 -1
  107. package/ecagent/dist/harness/session/memory-storage.js.map +0 -1
  108. package/ecagent/dist/harness/session/repo-utils.d.ts.map +0 -1
  109. package/ecagent/dist/harness/session/repo-utils.js.map +0 -1
  110. package/ecagent/dist/harness/session/session.d.ts.map +0 -1
  111. package/ecagent/dist/harness/session/session.js.map +0 -1
  112. package/ecagent/dist/harness/session/uuid.d.ts.map +0 -1
  113. package/ecagent/dist/harness/session/uuid.js.map +0 -1
  114. package/ecagent/dist/harness/skills.d.ts.map +0 -1
  115. package/ecagent/dist/harness/skills.js.map +0 -1
  116. package/ecagent/dist/harness/system-prompt.d.ts.map +0 -1
  117. package/ecagent/dist/harness/system-prompt.js.map +0 -1
  118. package/ecagent/dist/harness/types.d.ts.map +0 -1
  119. package/ecagent/dist/harness/types.js.map +0 -1
  120. package/ecagent/dist/harness/utils/shell-output.d.ts.map +0 -1
  121. package/ecagent/dist/harness/utils/shell-output.js.map +0 -1
  122. package/ecagent/dist/harness/utils/truncate.d.ts.map +0 -1
  123. package/ecagent/dist/harness/utils/truncate.js.map +0 -1
  124. package/ecagent/dist/index.d.ts.map +0 -1
  125. package/ecagent/dist/index.js.map +0 -1
  126. package/ecagent/dist/node.d.ts.map +0 -1
  127. package/ecagent/dist/node.js.map +0 -1
  128. package/ecagent/dist/proxy.d.ts.map +0 -1
  129. package/ecagent/dist/proxy.js.map +0 -1
  130. package/ecagent/dist/runtime/api/lazy.d.ts.map +0 -1
  131. package/ecagent/dist/runtime/api/lazy.js.map +0 -1
  132. package/ecagent/dist/runtime/api/pi-messages.d.ts.map +0 -1
  133. package/ecagent/dist/runtime/api/pi-messages.js.map +0 -1
  134. package/ecagent/dist/runtime/api/pi-messages.lazy.d.ts.map +0 -1
  135. package/ecagent/dist/runtime/api/pi-messages.lazy.js.map +0 -1
  136. package/ecagent/dist/runtime/auth/context.d.ts.map +0 -1
  137. package/ecagent/dist/runtime/auth/context.js.map +0 -1
  138. package/ecagent/dist/runtime/auth/credential-store.d.ts.map +0 -1
  139. package/ecagent/dist/runtime/auth/credential-store.js.map +0 -1
  140. package/ecagent/dist/runtime/auth/helpers.d.ts.map +0 -1
  141. package/ecagent/dist/runtime/auth/helpers.js.map +0 -1
  142. package/ecagent/dist/runtime/auth/resolve.d.ts.map +0 -1
  143. package/ecagent/dist/runtime/auth/resolve.js.map +0 -1
  144. package/ecagent/dist/runtime/auth/types.d.ts.map +0 -1
  145. package/ecagent/dist/runtime/auth/types.js.map +0 -1
  146. package/ecagent/dist/runtime/compat.d.ts.map +0 -1
  147. package/ecagent/dist/runtime/compat.js.map +0 -1
  148. package/ecagent/dist/runtime/index.d.ts.map +0 -1
  149. package/ecagent/dist/runtime/index.js.map +0 -1
  150. package/ecagent/dist/runtime/models.d.ts.map +0 -1
  151. package/ecagent/dist/runtime/models.js.map +0 -1
  152. package/ecagent/dist/runtime/oauth.d.ts.map +0 -1
  153. package/ecagent/dist/runtime/oauth.js.map +0 -1
  154. package/ecagent/dist/runtime/providers/faux.d.ts.map +0 -1
  155. package/ecagent/dist/runtime/providers/faux.js.map +0 -1
  156. package/ecagent/dist/runtime/types.d.ts.map +0 -1
  157. package/ecagent/dist/runtime/types.js.map +0 -1
  158. package/ecagent/dist/runtime/utils/diagnostics.d.ts.map +0 -1
  159. package/ecagent/dist/runtime/utils/diagnostics.js.map +0 -1
  160. package/ecagent/dist/runtime/utils/event-stream.d.ts.map +0 -1
  161. package/ecagent/dist/runtime/utils/event-stream.js.map +0 -1
  162. package/ecagent/dist/runtime/utils/headers.d.ts.map +0 -1
  163. package/ecagent/dist/runtime/utils/headers.js.map +0 -1
  164. package/ecagent/dist/runtime/utils/json-parse.d.ts.map +0 -1
  165. package/ecagent/dist/runtime/utils/json-parse.js.map +0 -1
  166. package/ecagent/dist/runtime/utils/oauth/types.d.ts.map +0 -1
  167. package/ecagent/dist/runtime/utils/oauth/types.js.map +0 -1
  168. package/ecagent/dist/runtime/utils/overflow.d.ts.map +0 -1
  169. package/ecagent/dist/runtime/utils/overflow.js.map +0 -1
  170. package/ecagent/dist/runtime/utils/provider-env.d.ts.map +0 -1
  171. package/ecagent/dist/runtime/utils/provider-env.js.map +0 -1
  172. package/ecagent/dist/runtime/utils/retry.d.ts.map +0 -1
  173. package/ecagent/dist/runtime/utils/retry.js.map +0 -1
  174. package/ecagent/dist/runtime/utils/typebox-helpers.d.ts.map +0 -1
  175. package/ecagent/dist/runtime/utils/typebox-helpers.js.map +0 -1
  176. package/ecagent/dist/runtime/utils/validation.d.ts.map +0 -1
  177. package/ecagent/dist/runtime/utils/validation.js.map +0 -1
  178. package/ecagent/dist/types.d.ts.map +0 -1
  179. package/ecagent/dist/types.js.map +0 -1
  180. package/ecagent/package.json +0 -93
  181. package/ecagent/src/agent-loop.ts +0 -792
  182. package/ecagent/src/agent.ts +0 -575
  183. package/ecagent/src/harness/agent-harness.ts +0 -1029
  184. package/ecagent/src/harness/compaction/branch-summarization.ts +0 -261
  185. package/ecagent/src/harness/compaction/compaction.ts +0 -753
  186. package/ecagent/src/harness/compaction/utils.ts +0 -144
  187. package/ecagent/src/harness/env/nodejs.ts +0 -569
  188. package/ecagent/src/harness/messages.ts +0 -164
  189. package/ecagent/src/harness/prompt-templates.ts +0 -267
  190. package/ecagent/src/harness/session/jsonl-repo.ts +0 -179
  191. package/ecagent/src/harness/session/jsonl-storage.ts +0 -314
  192. package/ecagent/src/harness/session/memory-repo.ts +0 -50
  193. package/ecagent/src/harness/session/memory-storage.ts +0 -133
  194. package/ecagent/src/harness/session/repo-utils.ts +0 -51
  195. package/ecagent/src/harness/session/session.ts +0 -338
  196. package/ecagent/src/harness/session/uuid.ts +0 -54
  197. package/ecagent/src/harness/skills.ts +0 -375
  198. package/ecagent/src/harness/system-prompt.ts +0 -34
  199. package/ecagent/src/harness/types.ts +0 -838
  200. package/ecagent/src/harness/utils/shell-output.ts +0 -135
  201. package/ecagent/src/harness/utils/truncate.ts +0 -344
  202. package/ecagent/src/index.ts +0 -46
  203. package/ecagent/src/node.ts +0 -2
  204. package/ecagent/src/proxy.ts +0 -367
  205. package/ecagent/src/runtime/api/lazy.ts +0 -70
  206. package/ecagent/src/runtime/api/pi-messages.lazy.ts +0 -4
  207. package/ecagent/src/runtime/api/pi-messages.ts +0 -436
  208. package/ecagent/src/runtime/auth/context.ts +0 -45
  209. package/ecagent/src/runtime/auth/credential-store.ts +0 -47
  210. package/ecagent/src/runtime/auth/helpers.ts +0 -46
  211. package/ecagent/src/runtime/auth/resolve.ts +0 -141
  212. package/ecagent/src/runtime/auth/types.ts +0 -182
  213. package/ecagent/src/runtime/compat.ts +0 -158
  214. package/ecagent/src/runtime/index.ts +0 -31
  215. package/ecagent/src/runtime/models.ts +0 -452
  216. package/ecagent/src/runtime/oauth.ts +0 -1
  217. package/ecagent/src/runtime/providers/faux.ts +0 -538
  218. package/ecagent/src/runtime/types.ts +0 -718
  219. package/ecagent/src/runtime/utils/diagnostics.ts +0 -45
  220. package/ecagent/src/runtime/utils/event-stream.ts +0 -88
  221. package/ecagent/src/runtime/utils/headers.ts +0 -18
  222. package/ecagent/src/runtime/utils/json-parse.ts +0 -124
  223. package/ecagent/src/runtime/utils/oauth/types.ts +0 -79
  224. package/ecagent/src/runtime/utils/overflow.ts +0 -165
  225. package/ecagent/src/runtime/utils/provider-env.ts +0 -52
  226. package/ecagent/src/runtime/utils/retry.ts +0 -101
  227. package/ecagent/src/runtime/utils/typebox-helpers.ts +0 -24
  228. package/ecagent/src/runtime/utils/validation.ts +0 -310
  229. package/ecagent/src/types.ts +0 -430
  230. package/ecagent/tsconfig.build.json +0 -32
package/dist/cli/init.js CHANGED
@@ -6,11 +6,12 @@ import { resolvePaths, ensureDataDirs } from '../paths.js';
6
6
  import { commandExists } from '../utils/cross-platform.js';
7
7
  import { scanInstances } from '../utils/instance-registry.js';
8
8
  import { saveDefaultsSafe, loadAllAgents, loadDaemonConfig, saveDaemonConfig } from '../config-store.js';
9
- import { generateControlAid, resolveControlIssuer } from '../aun/aid/control-aid.js';
9
+ import { generateControlAid, resolveControlAidDomain } from '../aun/aid/control-aid.js';
10
10
  import { getCodexAppServerAvailability, isCodexAppServerAvailable } from '../agents/codex-runner.js';
11
11
  import { resolveEcagentConfig } from '../agents/baseagent.js';
12
12
  import { defaultProjectsRoot } from '../utils/project-path.js';
13
13
  import { WEB_CLI_BIN, WEB_PACKAGE_LATEST } from '../product.js';
14
+ import { autostartInstalled, autostartPlatformLabel, configureAutostart } from '../utils/autostart.js';
14
15
  // ==================== Helpers ====================
15
16
  function ask(rl, question) {
16
17
  return new Promise(resolve => rl.question(question, resolve));
@@ -66,6 +67,40 @@ function buildDefaults(chosen, available, projectsDefaultPath) {
66
67
  function writeDefaults(chosen, available, projectsDefaultPath) {
67
68
  saveDefaultsSafe(buildDefaults(chosen, available, projectsDefaultPath));
68
69
  }
70
+ function hasCompletedInitConfig() {
71
+ const p = resolvePaths();
72
+ const daemon = loadDaemonConfig();
73
+ if (!daemon.aid || !Array.isArray(daemon.owners) || daemon.owners.length === 0)
74
+ return false;
75
+ try {
76
+ const defaults = JSON.parse(fs.readFileSync(p.defaultsConfig, 'utf8'));
77
+ return typeof defaults?.active_baseagent === 'string' && defaults.active_baseagent.trim().length > 0;
78
+ }
79
+ catch {
80
+ return false;
81
+ }
82
+ }
83
+ function isAutostartOnlyRequest(options) {
84
+ return options?.autoStart !== undefined
85
+ && !options.baseagent
86
+ && !options.force
87
+ && !options.owner
88
+ && options.projectpath === undefined
89
+ && options.ecweb === undefined
90
+ && options.format === undefined;
91
+ }
92
+ function applyExplicitAutostart(enabled, runtimeRoot) {
93
+ const result = configureAutostart(enabled, runtimeRoot);
94
+ if (!result.ok) {
95
+ console.log(`❌ ${autostartPlatformLabel() || '登录自启'}配置失败: ${result.error || '未知错误'}`);
96
+ }
97
+ else {
98
+ console.log(enabled
99
+ ? `✓ 已启用 ${autostartPlatformLabel() || '登录'}自启`
100
+ : `✓ 已禁用 ${autostartPlatformLabel() || '登录'}自启`);
101
+ }
102
+ return result.ok;
103
+ }
69
104
  /** 启动门禁判定:缺控制 AID 且处于交互式终端时,应进 init 向导补全。
70
105
  * 非 TTY(restart-monitor/systemd/管道)即使缺 aid 也不进 init(无法交互),由 daemon 侧 warn 兜底。 */
71
106
  export function needsControlAidInit(aid, isTty) {
@@ -128,14 +163,29 @@ export async function cmdInit(options) {
128
163
  ecweb: options.ecweb,
129
164
  force: options.force,
130
165
  format: options.format,
166
+ autoStart: options.autoStart,
131
167
  });
132
168
  return true;
133
169
  }
170
+ if (options?.autoStart === true && !autostartPlatformLabel()) {
171
+ console.log('❌ --auto-start 仅支持 Windows 和 macOS 主机');
172
+ return false;
173
+ }
134
174
  const p = resolvePaths();
135
175
  ensureDataDirs();
176
+ // A bound installation may toggle login autostart independently of the
177
+ // Baseagent setup wizard. This is also the path used by public installers.
178
+ if (isAutostartOnlyRequest(options) && hasCompletedInitConfig()) {
179
+ return applyExplicitAutostart(options.autoStart, p.root);
180
+ }
136
181
  // ── 1. 单进程互斥 ──
137
182
  const aliveMains = scanInstances().mains.filter(m => m.alive);
138
183
  if (aliveMains.length > 0) {
184
+ // Explicit autostart flags only change the login task; they do not need
185
+ // to rewrite init configuration or stop an already running daemon.
186
+ if (isAutostartOnlyRequest(options)) {
187
+ return applyExplicitAutostart(options.autoStart, p.root);
188
+ }
139
189
  const pids = aliveMains.map(m => m.record.pid).join(', ');
140
190
  console.log(`❌ EvolCore 正在运行 (PID: ${pids}),请先执行 ec stop`);
141
191
  return false;
@@ -207,7 +257,11 @@ export async function cmdInit(options) {
207
257
  await runInteractive();
208
258
  }
209
259
  // ── 共享 tail(单一出口):提示创建 agent + 生成控制 AID ──
210
- return initTail({ invokedByStart: options?.invokedByStart });
260
+ return initTail({
261
+ invokedByStart: options?.invokedByStart,
262
+ nonInteractive: options?.nonInteractive,
263
+ autoStart: options?.autoStart,
264
+ });
211
265
  // ── 内部函数 ──
212
266
  async function runInteractive() {
213
267
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
@@ -313,38 +367,35 @@ export async function initTail(options = {}) {
313
367
  const { agents } = loadAllAgents();
314
368
  if (agents.length === 0 && !options.invokedByStart) {
315
369
  console.log('\n提示:尚无 agent,运行以下命令创建:');
316
- console.log(' ec agent new <aid>.agentid.pub');
370
+ console.log(' ec agent new <aid>.example.com');
317
371
  }
318
372
  // 控制 AID:daemon 进程身份。缺失则生成并写回 daemon.json(幂等:已存在则跳过)。
319
373
  const daemonConfig = loadDaemonConfig();
320
374
  let controlAidReady = !!daemonConfig.aid;
375
+ let autoStartReady = true;
321
376
  if (daemonConfig.aid) {
322
- const targetIssuer = resolveControlIssuer();
323
- const currentIssuer = daemonConfig.aid.split('.').slice(1).join('.');
324
- if (currentIssuer !== targetIssuer) {
325
- console.log(`⚠️ 控制 AID issuer (${currentIssuer}) 与目标 issuer (${targetIssuer}) 不一致`);
326
- console.log(` 如需切换,请删除 daemon.json 中的 aid 字段后重新运行 init`);
327
- }
328
377
  console.log(`✓ 控制 AID 已存在: ${daemonConfig.aid}`);
329
378
  }
330
379
  else {
331
380
  try {
332
- const { aid } = await generateControlAid();
381
+ const aidDomain = resolveControlAidDomain(daemonConfig.aun?.defaultAidDomain);
382
+ const { aid } = await generateControlAid(aidDomain);
333
383
  saveDaemonConfig({ ...daemonConfig, $schema_version: daemonConfig.$schema_version ?? 1, aid });
334
384
  controlAidReady = true;
335
385
  console.log(`✓ 已生成控制 AID: ${aid}`);
336
386
  }
337
387
  catch (e) {
338
- console.error(`⚠️ 控制 AID 生成失败(Gateway 不可达?联网后重跑 ec init 补全): ${e?.message || e}`);
388
+ console.error(`⚠️ 控制 AID 生成失败(请检查 AID domain 配置或 Gateway 连通性): ${e?.message || e}`);
339
389
  controlAidReady = false;
340
390
  }
341
391
  }
342
392
  // 交互式询问:使用单一 readline 实例避免 stdin 状态混乱
343
- if (process.stdin.isTTY) {
393
+ if (!options.nonInteractive && process.stdin.isTTY) {
344
394
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
345
395
  try {
346
396
  await handleOwnersPrompt(rl);
347
397
  await handleEcwebPrompt(rl);
398
+ await handleAutostartPrompt(rl);
348
399
  }
349
400
  finally {
350
401
  try {
@@ -353,6 +404,38 @@ export async function initTail(options = {}) {
353
404
  catch { /* ignore */ }
354
405
  }
355
406
  }
407
+ else if (options.autoStart !== undefined) {
408
+ autoStartReady = applyAutostart(options.autoStart);
409
+ }
410
+ async function handleAutostartPrompt(rl) {
411
+ if (options.autoStart !== undefined) {
412
+ autoStartReady = applyAutostart(options.autoStart);
413
+ return;
414
+ }
415
+ if (!autostartPlatformLabel())
416
+ return;
417
+ if (autostartInstalled()) {
418
+ console.log(`✓ ${autostartPlatformLabel()} 登录自启已启用`);
419
+ return;
420
+ }
421
+ const platform = autostartPlatformLabel();
422
+ const answer = (await ask(rl, `\n是否启用 ${platform} 登录自启?[y/N] `)).trim().toLowerCase();
423
+ if (answer === 'y' || answer === 'yes')
424
+ autoStartReady = applyAutostart(true);
425
+ else
426
+ console.log(` 已跳过 ${platform} 登录自启(可日后重新运行 ec init 开启)`);
427
+ }
428
+ function applyAutostart(enabled) {
429
+ const result = configureAutostart(enabled, resolvePaths().root);
430
+ if (!result.ok) {
431
+ console.log(` ❌ ${autostartPlatformLabel() || '登录自启'}配置失败: ${result.error || '未知错误'}`);
432
+ return false;
433
+ }
434
+ console.log(enabled
435
+ ? ` ✓ 已启用 ${autostartPlatformLabel() || '登录'}自启`
436
+ : ` ✓ 已禁用 ${autostartPlatformLabel() || '登录'}自启`);
437
+ return true;
438
+ }
356
439
  async function handleOwnersPrompt(rl) {
357
440
  const daemonConfigForOwners = loadDaemonConfig();
358
441
  if (daemonConfigForOwners.aid && (!daemonConfigForOwners.owners || daemonConfigForOwners.owners.length === 0)) {
@@ -432,11 +515,13 @@ export async function initTail(options = {}) {
432
515
  }
433
516
  // ec start 会接着启动 daemon,并在其 ready 后给出可用的 Agent 创建入口。
434
517
  if (options.invokedByStart)
435
- return controlAidReady;
518
+ return controlAidReady && autoStartReady;
436
519
  if (!controlAidReady) {
437
520
  console.error('\n❌ EvolCore 初始化未完成:控制 AID 未配置。联网后重新运行 ec init。');
438
521
  return false;
439
522
  }
523
+ if (!autoStartReady)
524
+ return false;
440
525
  // 初始化完成总结
441
526
  console.log('\n✓ EvolCore 初始化完成');
442
527
  const finalCfg = loadDaemonConfig();
@@ -446,7 +531,7 @@ export async function initTail(options = {}) {
446
531
  const { agents: finalAgents } = loadAllAgents();
447
532
  if (finalAgents.length === 0) {
448
533
  console.log('\n📌 下一步:创建 agent');
449
- console.log(' ec agent new <your-aid>.agentid.pub');
534
+ console.log(' ec agent new <your-aid>.example.com');
450
535
  console.log(' ec init feishu # 绑定飞书');
451
536
  console.log(' ec start # 启动服务');
452
537
  }
@@ -530,6 +615,9 @@ export async function cmdInitNonInteractive(opts, dependencies = {}) {
530
615
  if (format !== undefined && format !== 'json') {
531
616
  fail('INVALID_FORMAT', `--format only supports json: ${format}`, EXIT_USAGE, format);
532
617
  }
618
+ if (opts.autoStart === true && !autostartPlatformLabel()) {
619
+ fail('AUTOSTART_UNSUPPORTED', '--auto-start is supported only on Windows and macOS hosts', EXIT_USAGE, format);
620
+ }
533
621
  // ── 1. owner 校验 ──
534
622
  if (!opts.owner) {
535
623
  fail('MISSING_OWNER', '--owner is required in non-interactive mode', EXIT_USAGE, format);
@@ -632,11 +720,12 @@ export async function cmdInitNonInteractive(opts, dependencies = {}) {
632
720
  let controlAid = existingCfg.aid;
633
721
  if (!controlAid) {
634
722
  try {
635
- const { aid } = await generateControlAid();
723
+ const aidDomain = resolveControlAidDomain(existingCfg.aun?.defaultAidDomain);
724
+ const { aid } = await generateControlAid(aidDomain);
636
725
  controlAid = aid;
637
726
  }
638
727
  catch (e) {
639
- fail('CONTROL_AID_CREATE_FAILED', `gateway unreachable: ${e?.message || e}`, EXIT_RUNTIME, format);
728
+ fail('CONTROL_AID_CREATE_FAILED', `control AID creation failed: ${e?.message || e}`, EXIT_RUNTIME, format);
640
729
  }
641
730
  }
642
731
  // ── 9. 写 daemon.json(aid + owners + ecweb)──
@@ -653,6 +742,12 @@ export async function cmdInitNonInteractive(opts, dependencies = {}) {
653
742
  catch (e) {
654
743
  fail('IO_ERROR', `failed to write daemon.json: ${e?.message || e}`, EXIT_RUNTIME, format);
655
744
  }
745
+ if (opts.autoStart !== undefined) {
746
+ const autostart = configureAutostart(opts.autoStart, p.root);
747
+ if (!autostart.ok) {
748
+ fail('AUTOSTART_CONFIG_FAILED', autostart.error || 'failed to configure login autostart', EXIT_RUNTIME, format);
749
+ }
750
+ }
656
751
  // ── 10. 输出 init.result ──
657
752
  const result = {
658
753
  type: 'init.result',
@@ -660,7 +755,7 @@ export async function cmdInitNonInteractive(opts, dependencies = {}) {
660
755
  controlAid: controlAid,
661
756
  ownerAid,
662
757
  owners: [ownerAid],
663
- ecwebEnabled: opts.ecweb === true,
758
+ ecwebEnabled: opts.ecweb === true || existingCfg.ecweb?.enabled === true,
664
759
  baseagent: chosenBaseagent,
665
760
  projectsDefaultPath: projectsDefaultPath ?? null,
666
761
  defaultsPath: p.defaultsConfig,
package/dist/cli/model.js CHANGED
@@ -205,7 +205,7 @@ function modelScopeFromSource(source) {
205
205
  return undefined;
206
206
  }
207
207
  function resolveModelConfig(sel, baseagent) {
208
- const effortKey = baseagent === 'codex' ? 'reasoning' : 'effort';
208
+ const effortKey = 'effort';
209
209
  const model = resolveEffectiveFieldWithSource(`baseagents.${baseagent}.model`, sel, { cache: true });
210
210
  const effort = resolveEffectiveFieldWithSource(`baseagents.${baseagent}.${effortKey}`, sel, { cache: true });
211
211
  const modelDecision = sel.role
@@ -448,12 +448,12 @@ async function cmdUse(args, formatJson) {
448
448
  const effortVal = effort === 'auto' ? null : (effort ?? undefined);
449
449
  if (isManagedTargetedWriteEnv(args)) {
450
450
  if (effortVal !== undefined) {
451
- const effortKey = ba === 'codex' ? 'reasoning' : 'effort';
451
+ const effortKey = 'effort';
452
452
  preflightManagedFieldOverride(sel, role, `baseagents.${ba}.${effortKey}`, effortVal, formatJson);
453
453
  }
454
454
  await setManagedModelField(sel, `baseagents.${ba}.model`, modelId, formatJson);
455
455
  if (effortVal !== undefined) {
456
- const effortKey = ba === 'codex' ? 'reasoning' : 'effort';
456
+ const effortKey = 'effort';
457
457
  await setManagedModelField(sel, `baseagents.${ba}.${effortKey}`, effortVal, formatJson);
458
458
  }
459
459
  describeWrite(scope, sel, modelId, effort ?? null, formatJson);
@@ -490,7 +490,7 @@ async function cmdEffort(args, formatJson) {
490
490
  const ba = activeBaseagent(sel.self);
491
491
  const val = level === 'auto' ? null : level;
492
492
  if (isManagedTargetedWriteEnv(args)) {
493
- const effortKey = ba === 'codex' ? 'reasoning' : 'effort';
493
+ const effortKey = 'effort';
494
494
  preflightManagedFieldOverride(sel, explicitOrDerivedRole(args, sel), `baseagents.${ba}.${effortKey}`, val, formatJson);
495
495
  await setManagedModelField(sel, `baseagents.${ba}.${effortKey}`, val, formatJson);
496
496
  describeWrite(scope, sel, null, level, formatJson);
@@ -520,7 +520,7 @@ async function cmdReset(args, formatJson) {
520
520
  const scope = determineWriteScope(sel, formatJson);
521
521
  const ba = activeBaseagent(sel.self);
522
522
  if (isManagedTargetedWriteEnv(args)) {
523
- const effortKey = ba === 'codex' ? 'reasoning' : 'effort';
523
+ const effortKey = 'effort';
524
524
  const role = explicitOrDerivedRole(args, sel);
525
525
  preflightManagedFieldOverride(sel, role, `baseagents.${ba}.model`, null, formatJson);
526
526
  preflightManagedFieldOverride(sel, role, `baseagents.${ba}.${effortKey}`, null, formatJson);
@@ -93,6 +93,37 @@ export async function cmdRestartMonitor() {
93
93
  await sleep(500);
94
94
  }
95
95
  }
96
+ // A stale instance record can disappear while the real Windows process is
97
+ // still alive. Refuse to create a second daemon when an unregistered process
98
+ // from this package is found; a duplicate would connect AUN and kick the
99
+ // existing session before failing on IPC/port ownership.
100
+ const runtimeOrphans = findOrphanProcesses().filter(o => {
101
+ if (o.confirmedTestDaemon)
102
+ return false;
103
+ const homeMatches = o.evolcoreHome && normalizePath(o.evolcoreHome) === normalizePath(p.root);
104
+ const packageMatches = !o.evolcoreHome && normalizePath(o.cmdline).includes(normalizePath(getPackageRoot()));
105
+ return Boolean(homeMatches || packageMatches);
106
+ });
107
+ if (runtimeOrphans.length > 0) {
108
+ const pids = runtimeOrphans.map(o => o.pid).join(', ');
109
+ log(`Stopping unregistered EvolCore process(es): ${pids}`);
110
+ for (const orphan of runtimeOrphans) {
111
+ platform.killProcess(orphan.pid, false);
112
+ }
113
+ const exited = await Promise.all(runtimeOrphans.map(async (orphan) => {
114
+ if (await platform.waitForProcessExit(orphan.pid, 10_000))
115
+ return true;
116
+ platform.killProcess(orphan.pid, true);
117
+ return platform.waitForProcessExit(orphan.pid, 5_000);
118
+ }));
119
+ if (exited.some(result => !result)) {
120
+ const message = `未登记的 EvolCore 进程仍未退出(PID: ${pids}),已停止重启以避免重复连接 AUN。`;
121
+ log(message);
122
+ await notifyDaemonOwners(p, `❌ ${message}`, log);
123
+ process.exit(1);
124
+ }
125
+ cleanupInstances();
126
+ }
96
127
  // restart-pending.json 只留给新主进程发送发起会话内的“重启成功”回执。
97
128
  const pendingFile = path.join(daemonControlDir(), 'restart-pending.json');
98
129
  // 等待所有活 main 进程退出(可能不止一个)
@@ -108,24 +139,24 @@ export async function cmdRestartMonitor() {
108
139
  }
109
140
  catch { }
110
141
  }
111
- await Promise.all(oldPids.map(oldPid => new Promise((resolve) => {
112
- let waited = 0;
113
- const interval = setInterval(() => {
114
- waited++;
115
- if (!platform.isProcessRunning(oldPid)) {
116
- clearInterval(interval);
117
- log(`Process ${oldPid} has exited`);
118
- resolve();
119
- return;
120
- }
121
- if (waited >= 30) {
122
- clearInterval(interval);
123
- log(`ERROR: Process ${oldPid} still running after 30s, force killing`);
124
- platform.killProcess(oldPid, true);
125
- resolve();
126
- }
127
- }, 1000);
128
- })));
142
+ const exited = await Promise.all(oldPids.map(async (oldPid) => {
143
+ if (await platform.waitForProcessExit(oldPid, 30_000)) {
144
+ log(`Process ${oldPid} has exited`);
145
+ return true;
146
+ }
147
+ log(`ERROR: Process ${oldPid} still running after 30s, force killing`);
148
+ platform.killProcess(oldPid, true);
149
+ const forced = await platform.waitForProcessExit(oldPid, 5_000);
150
+ if (!forced)
151
+ log(`ERROR: Process ${oldPid} survived forced termination`);
152
+ return forced;
153
+ }));
154
+ if (exited.some(result => !result)) {
155
+ log('❌ Existing daemon process could not be stopped; refusing to spawn a replacement');
156
+ await notifyDaemonOwners(p, '❌ 旧 EvolCore 进程未能退出,已停止重启,避免重复连接 AUN。', log);
157
+ cleanupPendingFile(pendingFile, log);
158
+ process.exit(1);
159
+ }
129
160
  await sleep(3000);
130
161
  cleanupInstances();
131
162
  }
@@ -206,10 +237,25 @@ export async function cmdRestartMonitor() {
206
237
  cleanupPendingFile(pendingFile, log);
207
238
  process.exit(1);
208
239
  }
240
+ if (hasStartupResourceConflict(p)) {
241
+ const message = '服务启动失败:IPC 管道或端口仍被占用,已跳过自动修复,避免重复连接 AUN。';
242
+ log(message);
243
+ await notifyDaemonOwners(p, `❌ ${message}`, log);
244
+ cleanupPendingFile(pendingFile, log);
245
+ process.exit(1);
246
+ }
209
247
  // 启动失败,进入 self-heal 循环
210
248
  log('❌ Service failed to start, entering self-heal loop');
211
249
  eventBus.publish({ type: 'self-heal:started', reason: 'Service failed to start after restart' });
212
250
  await notifyDaemonOwners(p, '⚠️ 服务启动失败,正在尝试自动修复...', log);
251
+ const claudePath = platform.resolveCommandPath('claude');
252
+ if (!claudePath) {
253
+ const message = '找不到 claude CLI,已停止自动修复;请检查后台进程 PATH。';
254
+ log(message);
255
+ await notifyDaemonOwners(p, `❌ ${message}`, log);
256
+ cleanupPendingFile(pendingFile, log);
257
+ process.exit(1);
258
+ }
213
259
  for (let attempt = 1; attempt <= MAX_HEAL_ATTEMPTS; attempt++) {
214
260
  // 前置检查:服务可能已被上一轮 claude 修复并启动
215
261
  if (isServiceAlive()) {
@@ -223,7 +269,7 @@ export async function cmdRestartMonitor() {
223
269
  log(`Self-heal attempt ${attempt}/${MAX_HEAL_ATTEMPTS}`);
224
270
  eventBus.publish({ type: 'self-heal:attempt', attemptNumber: attempt, maxAttempts: MAX_HEAL_ATTEMPTS });
225
271
  await notifyDaemonOwners(p, `🔧 自动修复中(第 ${attempt}/${MAX_HEAL_ATTEMPTS} 次)...`, log);
226
- const healed = await invokeClaude(p, attempt, MAX_HEAL_ATTEMPTS, HEAL_TIMEOUT, log);
272
+ const healed = await invokeClaude(claudePath, p, attempt, MAX_HEAL_ATTEMPTS, HEAL_TIMEOUT, log);
227
273
  // 后置检查:不管 invokeClaude 返回什么,都检查服务实际状态
228
274
  if (isServiceAlive()) {
229
275
  log(`✓ Service is running after attempt ${attempt}`);
@@ -290,6 +336,30 @@ async function sendHealSummary(p, attempts, log) {
290
336
  function sleep(ms) {
291
337
  return new Promise(resolve => setTimeout(resolve, ms));
292
338
  }
339
+ function normalizePath(value) {
340
+ return value.replace(/[\\/]+/g, '/').toLowerCase();
341
+ }
342
+ function hasStartupResourceConflict(p) {
343
+ let daemonLog = null;
344
+ try {
345
+ daemonLog = fs.readdirSync(p.logs, { withFileTypes: true })
346
+ .filter(entry => entry.isFile() && /^daemon-.*\.log$/.test(entry.name))
347
+ .sort((a, b) => b.name.localeCompare(a.name))
348
+ .slice(0, 1)
349
+ .map(entry => path.join(p.logs, entry.name))[0] ?? null;
350
+ }
351
+ catch { }
352
+ const files = [path.join(p.logs, 'stdout.log'), ...(daemonLog ? [daemonLog] : [])];
353
+ return files.some(file => {
354
+ try {
355
+ const content = fs.readFileSync(file, 'utf-8');
356
+ return /EADDRINUSE|address already in use|pipe-.*already in use/i.test(content.slice(-64 * 1024));
357
+ }
358
+ catch {
359
+ return false;
360
+ }
361
+ });
362
+ }
293
363
  function cleanupPendingFile(filePath, log) {
294
364
  try {
295
365
  if (fs.existsSync(filePath)) {
@@ -353,9 +423,17 @@ async function spawnAndWaitReady(p, log, timeout) {
353
423
  }
354
424
  }
355
425
  log(`Ready signal not received within ${timeout / 1000}s`);
356
- // 超时后杀掉进程
426
+ // A timeout is still a live-process race: do not let self-heal spawn a
427
+ // replacement while the timed-out daemon owns the IPC endpoint or a port.
357
428
  if (platform.isProcessRunning(childPid)) {
358
429
  platform.killProcess(childPid);
430
+ if (!await platform.waitForProcessExit(childPid, 5_000)) {
431
+ log(`Process ${childPid} survived graceful termination, forcing kill`);
432
+ platform.killProcess(childPid, true);
433
+ if (!await platform.waitForProcessExit(childPid, 5_000)) {
434
+ log(`ERROR: Process ${childPid} survived forced termination`);
435
+ }
436
+ }
359
437
  }
360
438
  cleanupInstances();
361
439
  return false;
@@ -363,7 +441,7 @@ async function spawnAndWaitReady(p, log, timeout) {
363
441
  /**
364
442
  * 调用 claude CLI 进行自动修复
365
443
  */
366
- async function invokeClaude(p, attempt, maxAttempts, timeout, log) {
444
+ async function invokeClaude(claudePath, p, attempt, maxAttempts, timeout, log) {
367
445
  const projectDir = getPackageRoot();
368
446
  const selfHealLog = p.selfHealLog;
369
447
  const stdoutLog = path.join(p.logs, 'stdout.log');
@@ -401,7 +479,7 @@ async function invokeClaude(p, attempt, maxAttempts, timeout, log) {
401
479
  注意:只修复导致启动失败的问题,不要做额外的重构或优化。`;
402
480
  try {
403
481
  log(`Invoking claude CLI (attempt ${attempt}, timeout ${timeout / 60000}min)...`);
404
- const { stdout, stderr } = await execFileAsync('claude', [
482
+ const { stdout, stderr } = await execFileAsync(claudePath, [
405
483
  '-p', prompt,
406
484
  '--allowedTools', 'Read,Write,Edit,Bash,Glob,Grep',
407
485
  '--output-format', 'text',
@@ -409,6 +487,8 @@ async function invokeClaude(p, attempt, maxAttempts, timeout, log) {
409
487
  ], {
410
488
  cwd: projectDir,
411
489
  timeout,
490
+ // Windows npm CLIs are .cmd shims; execFile needs a shell to run them.
491
+ shell: platform.isWindows,
412
492
  env: { ...process.env, CLAUDE_CODE_ENTRYPOINT: 'cli' },
413
493
  maxBuffer: 10 * 1024 * 1024,
414
494
  });
@@ -1,18 +1,19 @@
1
1
  import path from 'path';
2
2
  import { kitsRoleTemplatesDir } from '../paths.js';
3
3
  import { createErrorCapturingReader, fileCache } from '../core/daemon-file-cache.js';
4
- import { validateRoleConfig } from './role-schema.js';
4
+ import { normalizeRoleEffortCompat, validateRoleConfig } from './role-schema.js';
5
5
  import { expectedRoleRank } from './role-ranks.js';
6
6
  export const BUILTIN_ROLE_IDS = ['owner', 'admin', 'member', 'visitor'];
7
7
  export function validateBuiltinRoleTemplateDefinition(roleId, raw) {
8
- const issues = validateRoleConfig(raw);
8
+ const normalizedRaw = normalizeRoleEffortCompat(raw);
9
+ const issues = validateRoleConfig(normalizedRaw);
9
10
  if (issues.length) {
10
11
  return {
11
12
  definition: null,
12
13
  error: `built-in role template failed validation: ${issues.map(issue => `${issue.field} ${issue.message}`).join('; ')}`,
13
14
  };
14
15
  }
15
- const { $schema_version: _schemaVersion, ...definition } = raw;
16
+ const { $schema_version: _schemaVersion, ...definition } = normalizedRaw;
16
17
  if (BUILTIN_ROLE_IDS.includes(roleId)) {
17
18
  const expectedRank = expectedRoleRank(roleId);
18
19
  if (definition.rank !== expectedRank) {
@@ -75,7 +75,7 @@ export function resolveConfigFieldRule(fieldPath) {
75
75
  const [, baseagent, leaf] = parts;
76
76
  if (!SUPPORTED_BASEAGENTS.has(baseagent))
77
77
  return { class: 'unknown' };
78
- const permissionKey = `baseagents.${baseagent}.${leaf}`;
78
+ const permissionKey = `baseagents.${baseagent}.${leaf === 'reasoning' ? 'effort' : leaf}`;
79
79
  if (leaf === 'model' && parts.length === 3)
80
80
  return scalar(permissionKey, 'string');
81
81
  if ((leaf === 'effort' || leaf === 'reasoning') && parts.length === 3)
@@ -178,7 +178,11 @@ export function resolveRoleFieldPermission(permissions, fieldPath) {
178
178
  const rule = resolveConfigFieldRule(fieldPath);
179
179
  if (!rule.rolePermissionKey)
180
180
  return undefined;
181
- return permissions[fieldPath] ?? permissions[rule.rolePermissionKey];
181
+ return permissions[fieldPath]
182
+ ?? permissions[rule.rolePermissionKey]
183
+ ?? (rule.rolePermissionKey === 'baseagents.codex.effort'
184
+ ? permissions['baseagents.codex.reasoning']
185
+ : undefined);
182
186
  }
183
187
  /**
184
188
  * Map relation-scoped config fields back to their public command capability.