evolcore 0.0.19 → 0.0.21
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.
- package/CHANGELOG.md +43 -1
- package/README.md +60 -9
- package/bin/install-codex-managed-hooks.mjs +61 -0
- package/dist/agents/baseagent.js +10 -6
- package/dist/agents/claude-runner.js +383 -111
- package/dist/agents/codex-app-server-client.js +10 -2
- package/dist/agents/codex-runner.js +405 -137
- package/dist/agents/ecagent-runner.js +174 -63
- package/dist/agents/gemini-runner.js +130 -30
- package/dist/agents/request-identity.js +25 -0
- package/dist/agents/runner-types.js +19 -0
- package/dist/aun/aid/agentmd.js +66 -2
- package/dist/aun/aid/identity.js +4 -1
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/msg/group.js +86 -9
- package/dist/aun/msg/history.js +213 -36
- package/dist/aun/msg/managed-operation.js +58 -9
- package/dist/aun/msg/p2p.js +26 -11
- package/dist/aun/outbox.js +295 -68
- package/dist/aun/service-proxy.js +43 -25
- package/dist/channels/aun.js +1004 -273
- package/dist/channels/daemon.js +6 -1
- package/dist/cli/agent-command.js +4 -3
- package/dist/cli/agent.js +66 -56
- package/dist/cli/aun-commands.js +177 -42
- package/dist/cli/command-log.js +10 -11
- package/dist/cli/contact.js +1 -0
- package/dist/cli/daemon-commands.js +81 -121
- package/dist/cli/index.js +1 -0
- package/dist/cli/init.js +76 -24
- package/dist/cli/restart-monitor.js +3 -3
- package/dist/cli/task-context.js +46 -0
- package/dist/cli/trigger-command.js +1 -1
- package/dist/cli/watch-logs.js +10 -3
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-field-policy.js +16 -5
- package/dist/config/config-manager.js +226 -24
- package/dist/config/config-operation-service.js +1 -2
- package/dist/config/contact-operation-service.js +32 -1
- package/dist/config/contact-request-service.js +44 -0
- package/dist/config/daemon-services.js +186 -0
- package/dist/config/gateway-config.js +29 -16
- package/dist/config/lifecycle.js +16 -5
- package/dist/config/role-service.js +54 -3
- package/dist/config/schema-migration.js +550 -0
- package/dist/config-store.js +161 -12
- package/dist/core/agent-application-service.js +279 -0
- package/dist/core/audit/log-integrity.js +102 -0
- package/dist/core/auth/agent-delegation.js +31 -1
- package/dist/core/auth/auth-gateway.js +33 -4
- package/dist/core/auth/authorization-audit.js +155 -4
- package/dist/core/auth/operation-authorizer.js +41 -1
- package/dist/core/auth/operation-catalog.js +9 -1
- package/dist/core/bootstrap-messages.js +2 -2
- package/dist/core/bootstrap-service.js +27 -38
- package/dist/core/causation/aun-association.js +7 -4
- package/dist/core/channel-loader.js +0 -2
- package/dist/core/command/agent-control.js +56 -16
- package/dist/core/command/command-handler.js +290 -44
- package/dist/core/command/connect-menu.js +3 -4
- package/dist/core/command/group-menu.js +5 -7
- package/dist/core/command/menu-handler.js +279 -80
- package/dist/core/command/role-menu.js +21 -11
- package/dist/core/command/slash-gate.js +85 -18
- package/dist/core/command/slash-handler.js +350 -32
- package/dist/core/event-catalog.js +5 -0
- package/dist/core/evolagent.js +9 -4
- package/dist/core/handoff/dispatcher.js +4 -0
- package/dist/core/handoff/runtime.js +10 -0
- package/dist/core/handoff/store.js +32 -9
- package/dist/core/inference/text-inference.js +7 -15
- package/dist/core/message/im-renderer.js +83 -84
- package/dist/core/message/{inbound-admission.js → message-admission.js} +51 -1
- package/dist/core/message/message-bridge.js +124 -10
- package/dist/core/message/message-log.js +14 -7
- package/dist/core/message/message-queue.js +206 -16
- package/dist/core/message/message-utils.js +12 -5
- package/dist/core/message/response-engine.js +495 -72
- package/dist/core/message/send-receipt.js +1 -0
- package/dist/core/message/stream-debouncer.js +9 -2
- package/dist/core/model/model-catalog.js +23 -15
- package/dist/core/model/model-diagnostics.js +28 -10
- package/dist/core/permission/approval-gateway.js +180 -6
- package/dist/core/permission/ec-command-parser.js +465 -56
- package/dist/core/permission/mode.js +18 -3
- package/dist/core/{protected-paths.js → permission/protected-paths.js} +17 -5
- package/dist/core/permission/readonly-shell-query.js +263 -9
- package/dist/core/permission/sandbox-runtime.js +205 -13
- package/dist/core/permission/tool-policy.js +673 -62
- package/dist/core/relation/peer-identity.js +18 -0
- package/dist/core/session/session-fs-store.js +154 -5
- package/dist/core/session/session-manager.js +299 -30
- package/dist/core/session/session-renew.js +19 -12
- package/dist/core/session/session-turn-coordinator.js +11 -4
- package/dist/eck/kit-renderer.js +18 -9
- package/dist/index.js +283 -65
- package/dist/ipc.js +374 -24
- package/dist/paths.js +64 -7
- package/dist/response-system/context-builder.js +1 -7
- package/dist/trigger/anomaly-store.js +1 -0
- package/dist/trigger/feedback.js +56 -5
- package/dist/trigger/history.js +79 -4
- package/dist/trigger/legacy-session-history.js +2 -2
- package/dist/trigger/parser.js +3 -2
- package/dist/trigger/validation.js +6 -1
- package/dist/utils/atomic-write.js +27 -0
- package/dist/utils/ecweb-utils.js +16 -2
- package/dist/utils/error-utils.js +4 -1
- package/dist/utils/logger.js +21 -2
- package/dist/utils/process-tree-stats.js +24 -4
- package/dist/utils/process-tree-worker.js +31 -0
- package/dist/utils/project-path.js +1 -2
- package/dist/utils/stats.js +52 -18
- package/dist/utils/welcome.js +2 -2
- package/kits/docs/INDEX.md +1 -1
- package/kits/docs/evolcore/INDEX.md +1 -1
- package/kits/docs/evolcore/contact.md +7 -1
- package/kits/docs/evolcore/msg.md +16 -0
- package/kits/rules/01-overview.md +1 -1
- package/kits/rules/03-identity.md +1 -1
- package/kits/rules/05-venue.md +1 -1
- package/kits/schemas/_meta.json +10 -5
- package/kits/schemas/agent-config.schema.10.json +2 -1
- package/kits/schemas/agent-config.schema.11.json +421 -0
- package/kits/schemas/daemon.schema.5.json +135 -0
- package/kits/schemas/daemon.schema.6.json +131 -0
- package/kits/schemas/defaults.schema.5.json +119 -0
- package/kits/schemas/migrations/README.md +3 -1
- package/kits/schemas/relation-config.schema.8.json +13 -0
- package/kits/schemas/role-config.schema.1.json +1 -2
- package/kits/schemas/single-session.schema.3.json +32 -0
- package/kits/templates/roles/admin.json +1 -0
- package/kits/templates/roles/member.json +1 -0
- package/kits/templates/roles/visitor.json +1 -0
- package/package.json +7 -3
- package/skills/eclink/SKILL.md +2 -0
|
@@ -18,7 +18,7 @@ import { tryUpgrade, tryUpgradeGlobalPkg, resolveGlobalPkg } from '../utils/npm-
|
|
|
18
18
|
import { ecwebVersionRequirementError, fetchEcwebPairCode, resolveEcwebLaunchCommand } from '../utils/ecweb-utils.js';
|
|
19
19
|
import { isHostChinese } from '../utils/locale.js';
|
|
20
20
|
import { scanInstances, cleanupInstances, findOrphanProcesses, killOrphans } from '../utils/instance-registry.js';
|
|
21
|
-
import { filterLogFiles, deriveLogTypes, computePreChecked, validateLogTypes, shortLogName as shortLogNameLocal } from './watch-logs.js';
|
|
21
|
+
import { filterLogFiles, deriveLogTypes, computePreChecked, validateLogTypes, shortLogName as shortLogNameLocal, isWatchLogFile } from './watch-logs.js';
|
|
22
22
|
import { displaySessionTitle } from '../core/session/session-title.js';
|
|
23
23
|
import { isNonAgentSessionChannel } from '../core/system-channels.js';
|
|
24
24
|
import { printCodeStats } from './code-stats.js';
|
|
@@ -29,6 +29,7 @@ import { rotateStdoutLog } from '../utils/log-writer.js';
|
|
|
29
29
|
import { inspectDataMigrationRequirement } from '../core/data-migration.js';
|
|
30
30
|
import { isInitCancelledError, printInitCancelHint } from './init-cancel.js';
|
|
31
31
|
import { listCodexAppServerProcesses, unregisterCodexAppServerProcess } from '../utils/codex-app-server-registry.js';
|
|
32
|
+
import { DEFAULT_ECWEB_PORT, ecwebService, setEcwebEnabled } from '../config/daemon-services.js';
|
|
32
33
|
const execFileAsync = promisify(execFile);
|
|
33
34
|
async function runAutomaticInit(action) {
|
|
34
35
|
try {
|
|
@@ -58,15 +59,21 @@ function printNoSelfAgentHints(options) {
|
|
|
58
59
|
console.log(` - ${skippedAgent.dirName}: ${skippedAgent.reason}`);
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
|
-
|
|
62
|
+
// Windows named-pipe connection setup can take a couple of seconds on a busy
|
|
63
|
+
// host (especially on the first request after the daemon starts). A one-second
|
|
64
|
+
// probe causes `ec stop` to skip the IPC shutdown path and fall back to
|
|
65
|
+
// taskkill, even though the daemon is healthy and able to shut down gracefully.
|
|
66
|
+
const DAEMON_PROBE_TIMEOUT_MS = platform.isWindows ? 5_000 : 1_000;
|
|
67
|
+
export const DAEMON_SHUTDOWN_TIMEOUT_MS = platform.isWindows ? 10_000 : 3_000;
|
|
68
|
+
async function probeDaemon(socketPath, timeoutMs = DAEMON_PROBE_TIMEOUT_MS) {
|
|
62
69
|
const response = await ipcQuery(socketPath, { type: 'ping' }, timeoutMs);
|
|
63
70
|
if (response?.pong !== true || !Number.isInteger(response.pid) || response.pid <= 0)
|
|
64
71
|
return null;
|
|
65
72
|
return response;
|
|
66
73
|
}
|
|
67
74
|
/** Ask the daemon to run its own graceful shutdown lifecycle. */
|
|
68
|
-
export async function requestDaemonShutdown(socketPath, timeoutMs =
|
|
69
|
-
const daemon = await probeDaemon(socketPath, Math.min(timeoutMs,
|
|
75
|
+
export async function requestDaemonShutdown(socketPath, timeoutMs = DAEMON_SHUTDOWN_TIMEOUT_MS, reason = 'cli', expectedPids) {
|
|
76
|
+
const daemon = await probeDaemon(socketPath, Math.min(timeoutMs, DAEMON_PROBE_TIMEOUT_MS));
|
|
70
77
|
if (!daemon || (expectedPids && !expectedPids.has(daemon.pid)))
|
|
71
78
|
return null;
|
|
72
79
|
const response = await ipcQuery(socketPath, {
|
|
@@ -140,11 +147,6 @@ function buildDaemonEnv(p, opts) {
|
|
|
140
147
|
...(opts.bindBootstrap ? { EVOLCORE_BIND_BOOTSTRAP: '1' } : {}),
|
|
141
148
|
};
|
|
142
149
|
}
|
|
143
|
-
export function serviceProxyNeedsEcweb(cfg) {
|
|
144
|
-
if (!cfg.serviceProxy?.enabled)
|
|
145
|
-
return false;
|
|
146
|
-
return (cfg.serviceProxy.services ?? []).some(s => s.enabled !== false && s.source === 'ecweb');
|
|
147
|
-
}
|
|
148
150
|
function latestSourceMtime(dir) {
|
|
149
151
|
let latest = 0;
|
|
150
152
|
try {
|
|
@@ -431,8 +433,6 @@ export async function cmdStart(opts = {}) {
|
|
|
431
433
|
console.log(` Rotated: stdout.log -> ${path.basename(stdoutRotation.rotatedPath)}`);
|
|
432
434
|
}
|
|
433
435
|
cleanEnv();
|
|
434
|
-
// 在启动前确保 serviceProxy 配置完整(如果 ecweb 启用但 serviceProxy 未配置)
|
|
435
|
-
ensureServiceProxyConfigBeforeStart(p);
|
|
436
436
|
// 删除旧的 ready signal
|
|
437
437
|
try {
|
|
438
438
|
fs.unlinkSync(p.readySignal);
|
|
@@ -561,7 +561,7 @@ export async function cmdStart(opts = {}) {
|
|
|
561
561
|
}
|
|
562
562
|
console.log('');
|
|
563
563
|
// 代码统计仅在开发环境显示(EVOLCORE_HOME 指向包目录)
|
|
564
|
-
if (resolveRoot() === getPackageRoot()) {
|
|
564
|
+
if (path.normalize(resolveRoot()) === path.normalize(getPackageRoot())) {
|
|
565
565
|
printCodeStats(getPackageRoot(), p.logs);
|
|
566
566
|
}
|
|
567
567
|
console.log(`⏱ done in ${((Date.now() - cmdStartedAt) / 1000).toFixed(1)}s`);
|
|
@@ -654,7 +654,7 @@ export async function cmdStop() {
|
|
|
654
654
|
console.log(`⚠ 检测到未登记的 EvolCore 进程,将一并停止: ${runtimeOrphans.map(orphan => orphan.pid).join(', ')}`);
|
|
655
655
|
}
|
|
656
656
|
const gracefulPid = ping && pids.has(ping.pid)
|
|
657
|
-
? await requestDaemonShutdown(p.socket,
|
|
657
|
+
? await requestDaemonShutdown(p.socket, DAEMON_SHUTDOWN_TIMEOUT_MS, 'ec stop', pids)
|
|
658
658
|
: null;
|
|
659
659
|
const stopped = await Promise.all([...pids].map(pid => stopPid(pid, pid === gracefulPid)));
|
|
660
660
|
if (stopped.some(result => !result)) {
|
|
@@ -726,7 +726,7 @@ export async function cmdRestart(opts = {}) {
|
|
|
726
726
|
const mainPids = new Set(aliveMains.map(entry => entry.record.pid));
|
|
727
727
|
const currentPing = await probeDaemon(socketPath);
|
|
728
728
|
const gracefulPid = currentPing && mainPids.has(currentPing.pid)
|
|
729
|
-
? await requestDaemonShutdown(socketPath,
|
|
729
|
+
? await requestDaemonShutdown(socketPath, DAEMON_SHUTDOWN_TIMEOUT_MS, 'ec restart', mainPids)
|
|
730
730
|
: null;
|
|
731
731
|
const stopped = await Promise.all(aliveMains.map(m => stopPid(m.record.pid, m.record.pid === gracefulPid)));
|
|
732
732
|
if (stopped.some(result => !result)) {
|
|
@@ -759,7 +759,7 @@ export async function cmdRestart(opts = {}) {
|
|
|
759
759
|
const orphanPids = new Set(runtimeOrphans.map(orphan => orphan.pid));
|
|
760
760
|
const currentPing = await probeDaemon(socketPath);
|
|
761
761
|
const gracefulPid = currentPing && orphanPids.has(currentPing.pid)
|
|
762
|
-
? await requestDaemonShutdown(socketPath,
|
|
762
|
+
? await requestDaemonShutdown(socketPath, DAEMON_SHUTDOWN_TIMEOUT_MS, 'ec restart', orphanPids)
|
|
763
763
|
: null;
|
|
764
764
|
const stopped = await Promise.all(runtimeOrphans.map(o => stopPid(o.pid, o.pid === gracefulPid)));
|
|
765
765
|
if (stopped.some(result => !result)) {
|
|
@@ -802,7 +802,7 @@ export async function cmdRestart(opts = {}) {
|
|
|
802
802
|
console.log('🔄 Restart monitor started, waiting for service to come back online...');
|
|
803
803
|
await sleep(2000);
|
|
804
804
|
// 代码统计(开发环境)
|
|
805
|
-
if (resolveRoot() === getPackageRoot()) {
|
|
805
|
+
if (path.normalize(resolveRoot()) === path.normalize(getPackageRoot())) {
|
|
806
806
|
console.log('');
|
|
807
807
|
printCodeStats(getPackageRoot(), resolvePaths().logs);
|
|
808
808
|
}
|
|
@@ -969,7 +969,17 @@ export async function cmdStatus() {
|
|
|
969
969
|
const p = resolvePaths();
|
|
970
970
|
const status = scanInstances();
|
|
971
971
|
const aliveMains = status.mains.filter(m => m.alive);
|
|
972
|
-
|
|
972
|
+
// Start all read-only daemon queries together. On a busy Windows host each
|
|
973
|
+
// query can otherwise wait behind the same IPC/event-loop delay and make the
|
|
974
|
+
// total `ec status` time the sum of several individual timeouts.
|
|
975
|
+
const pingPromise = probeDaemon(p.socket).catch(() => null);
|
|
976
|
+
const statusPromise = ipcQuery(p.socket, { type: 'status' }).catch(() => null);
|
|
977
|
+
const aidsPromise = ipcQuery(p.socket, { type: 'aun-aids' }).catch(() => null);
|
|
978
|
+
const agentsPromise = ipcQuery(p.socket, { type: 'evolagent.list' }).catch(() => null);
|
|
979
|
+
// Only the ping is needed to decide whether to show the live-daemon
|
|
980
|
+
// sections. The remaining responses continue in parallel while local
|
|
981
|
+
// process/session information is rendered.
|
|
982
|
+
const ping = await pingPromise;
|
|
973
983
|
const pid = ping?.pid ?? (aliveMains.length > 0 ? aliveMains[0].record.pid : null);
|
|
974
984
|
const processVisible = !!pid && aliveMains.some(entry => entry.record.pid === pid);
|
|
975
985
|
if (aliveMains.length > 1) {
|
|
@@ -1125,42 +1135,47 @@ export async function cmdStatus() {
|
|
|
1125
1135
|
}
|
|
1126
1136
|
// Channel status. A running daemon is authoritative and avoids reading
|
|
1127
1137
|
// protected config files from the CLI's potentially sandboxed process.
|
|
1138
|
+
let agentsResponse = null;
|
|
1128
1139
|
if (pid) {
|
|
1129
1140
|
console.log('');
|
|
1130
|
-
const
|
|
1131
|
-
|
|
1141
|
+
const [statusResponse, aidsResponse, agentResponse] = await Promise.all([
|
|
1142
|
+
statusPromise,
|
|
1143
|
+
aidsPromise,
|
|
1144
|
+
agentsPromise,
|
|
1145
|
+
]);
|
|
1146
|
+
agentsResponse = agentResponse;
|
|
1147
|
+
if (statusResponse) {
|
|
1132
1148
|
// 🔑 AUN AIDs 表格(详细 AUN 实例状态)
|
|
1133
1149
|
try {
|
|
1134
|
-
|
|
1135
|
-
if (aidsResp?.ok && aidsResp.aids?.length > 0) {
|
|
1150
|
+
if (aidsResponse?.ok && aidsResponse.aids?.length > 0) {
|
|
1136
1151
|
console.log('🔑 AUN AIDs:');
|
|
1137
|
-
renderAunAidsTable(
|
|
1152
|
+
renderAunAidsTable(aidsResponse.aids);
|
|
1138
1153
|
}
|
|
1139
1154
|
}
|
|
1140
1155
|
catch { /* ignore */ }
|
|
1141
1156
|
// 控制 AID(daemon 进程身份)状态
|
|
1142
|
-
if (
|
|
1143
|
-
const state =
|
|
1144
|
-
console.log(`control: ${
|
|
1157
|
+
if (statusResponse.controlAid) {
|
|
1158
|
+
const state = statusResponse.controlAid.connected ? 'connected' : 'disconnected';
|
|
1159
|
+
console.log(`control: ${statusResponse.controlAid.aid} [${state}]`);
|
|
1145
1160
|
}
|
|
1146
1161
|
else {
|
|
1147
1162
|
console.log('control: not configured');
|
|
1148
1163
|
}
|
|
1149
|
-
if (
|
|
1164
|
+
if (statusResponse.stats) {
|
|
1150
1165
|
console.log('');
|
|
1151
1166
|
console.log('📊 Last hour:');
|
|
1152
|
-
console.log(` Messages: ${
|
|
1153
|
-
if (
|
|
1154
|
-
console.log(` Errors: ${
|
|
1155
|
-
if (
|
|
1156
|
-
console.log(` Avg response: ${(
|
|
1167
|
+
console.log(` Messages: ${statusResponse.stats.received} received, ${statusResponse.stats.completed} completed`);
|
|
1168
|
+
if (statusResponse.stats.errors > 0)
|
|
1169
|
+
console.log(` Errors: ${statusResponse.stats.errors}`);
|
|
1170
|
+
if (statusResponse.stats.completed > 0)
|
|
1171
|
+
console.log(` Avg response: ${(statusResponse.stats.avgResponseMs / 1000).toFixed(1)}s`);
|
|
1157
1172
|
}
|
|
1158
1173
|
}
|
|
1159
1174
|
else {
|
|
1160
1175
|
// IPC unreachable but PID exists — fall back to local config if readable.
|
|
1161
1176
|
const config = readStatusDefaultsConfig(p.defaultsConfig);
|
|
1162
1177
|
if (config) {
|
|
1163
|
-
console.log('🔌 Channels (
|
|
1178
|
+
console.log('🔌 Channels (live status unavailable; configuration is stale):');
|
|
1164
1179
|
showConfigChannels(config);
|
|
1165
1180
|
}
|
|
1166
1181
|
}
|
|
@@ -1176,7 +1191,7 @@ export async function cmdStatus() {
|
|
|
1176
1191
|
// EvolAgent summary (via IPC, only when running)
|
|
1177
1192
|
if (pid) {
|
|
1178
1193
|
try {
|
|
1179
|
-
const agentResult =
|
|
1194
|
+
const agentResult = agentsResponse;
|
|
1180
1195
|
if (agentResult?.ok && agentResult.agents?.length > 0) {
|
|
1181
1196
|
const agents = agentResult.agents;
|
|
1182
1197
|
if (agents.length > 0) {
|
|
@@ -1190,6 +1205,22 @@ export async function cmdStatus() {
|
|
|
1190
1205
|
}
|
|
1191
1206
|
}
|
|
1192
1207
|
}
|
|
1208
|
+
else {
|
|
1209
|
+
const { EvolAgentRegistry } = await import('../core/evolagent-registry.js');
|
|
1210
|
+
const diskRegistry = new EvolAgentRegistry(p.agentsDir);
|
|
1211
|
+
diskRegistry.loadAll();
|
|
1212
|
+
const agents = diskRegistry.list();
|
|
1213
|
+
if (agents.length > 0) {
|
|
1214
|
+
console.log('');
|
|
1215
|
+
console.log('🤖 EvolAgents (live status unavailable; stale configuration):');
|
|
1216
|
+
for (const a of agents) {
|
|
1217
|
+
const diskStatus = a.status === 'disabled' || a.status === 'error' ? a.status : 'unknown';
|
|
1218
|
+
const channels = summarizeChannelFingerprints(a.channels || []);
|
|
1219
|
+
const shortName = a.name.replace(/\.agentid\.pub$/, '');
|
|
1220
|
+
console.log(` ? ${shortName.padEnd(20)} ${`${diskStatus} (stale)`.padEnd(18)} ${channels}`);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1193
1224
|
}
|
|
1194
1225
|
catch {
|
|
1195
1226
|
// IPC query for agents failed — skip section
|
|
@@ -1783,10 +1814,10 @@ async function cmdWatchLogsFlow() {
|
|
|
1783
1814
|
console.log(`❌ Log directory not found: ${p.logs}`);
|
|
1784
1815
|
process.exit(1);
|
|
1785
1816
|
}
|
|
1786
|
-
const files = fs.readdirSync(p.logs).filter(
|
|
1817
|
+
const files = fs.readdirSync(p.logs).filter(isWatchLogFile);
|
|
1787
1818
|
const types = deriveLogTypes(files);
|
|
1788
1819
|
if (types.length === 0) {
|
|
1789
|
-
console.log(`⚠ ${p.logs}
|
|
1820
|
+
console.log(`⚠ ${p.logs} 下暂无可监听日志文件`);
|
|
1790
1821
|
return;
|
|
1791
1822
|
}
|
|
1792
1823
|
const fileCount = new Map();
|
|
@@ -1857,7 +1888,7 @@ function cmdWatch(filterTypes) {
|
|
|
1857
1888
|
return c;
|
|
1858
1889
|
};
|
|
1859
1890
|
const listLogs = () => {
|
|
1860
|
-
const all = fs.readdirSync(p.logs).filter(
|
|
1891
|
+
const all = fs.readdirSync(p.logs).filter(isWatchLogFile).map(f => path.join(p.logs, f));
|
|
1861
1892
|
return filterLogFiles(all, filterTypes);
|
|
1862
1893
|
};
|
|
1863
1894
|
const shortName = shortLogNameLocal;
|
|
@@ -1879,7 +1910,7 @@ function cmdWatch(filterTypes) {
|
|
|
1879
1910
|
const content = formatWatchContent(line);
|
|
1880
1911
|
return `${timeStr} ${paddedName} ${content}`;
|
|
1881
1912
|
};
|
|
1882
|
-
console.log(`🔭 Watching ${p.logs}
|
|
1913
|
+
console.log(`🔭 Watching ${p.logs}/* log files (ESC to stop)\n`);
|
|
1883
1914
|
// 显示当前实例信息和 AID 状态
|
|
1884
1915
|
const instStatus = scanInstances();
|
|
1885
1916
|
const aliveMainEntries = instStatus.mains.filter(m => m.alive);
|
|
@@ -2502,9 +2533,9 @@ async function waitForExistingEcweb(p, timeoutMs = 3_000) {
|
|
|
2502
2533
|
async function printEcwebStatus(p) {
|
|
2503
2534
|
try {
|
|
2504
2535
|
const cfg = loadDaemonConfig();
|
|
2505
|
-
if (cfg.
|
|
2536
|
+
if (ecwebService(cfg.services)?.enabled === false) {
|
|
2506
2537
|
console.log('');
|
|
2507
|
-
console.log('🔭 ECWeb: 已禁用 (daemon.json → ecweb.enabled: false)');
|
|
2538
|
+
console.log('🔭 ECWeb: 已禁用 (daemon.json → services[name=ecweb].enabled: false)');
|
|
2508
2539
|
return;
|
|
2509
2540
|
}
|
|
2510
2541
|
}
|
|
@@ -2602,7 +2633,7 @@ export function stopCodexAppServerOrphans() {
|
|
|
2602
2633
|
/** 若 ecweb 在运行则杀掉并确认 pid/端口都已释放。 */
|
|
2603
2634
|
async function stopEcwebIfRunning(p) {
|
|
2604
2635
|
const alive = findAliveEcweb(p);
|
|
2605
|
-
const port = loadDaemonConfig().
|
|
2636
|
+
const port = ecwebService(loadDaemonConfig().services)?.port ?? DEFAULT_ECWEB_PORT;
|
|
2606
2637
|
const pids = new Set([
|
|
2607
2638
|
...(alive ? [alive.pid] : []),
|
|
2608
2639
|
...platform.findProcessByPort(port),
|
|
@@ -2628,78 +2659,6 @@ async function waitForPortRelease(port, timeoutMs) {
|
|
|
2628
2659
|
}
|
|
2629
2660
|
return platform.findProcessByPort(port).length === 0;
|
|
2630
2661
|
}
|
|
2631
|
-
/**
|
|
2632
|
-
* 后台 detached 启动 ecweb;若已运行则先停再启(确保加载最新代码)。
|
|
2633
|
-
* 启动后轮询端口确认 HTTP 服务真正就绪,打印明确的成功/失败结论(而非模糊的「已在后台启动」状态描述)。
|
|
2634
|
-
* 返回 true=本次确实启动成功,false=未启用/未安装/启动失败。
|
|
2635
|
-
*/
|
|
2636
|
-
/**
|
|
2637
|
-
* 启动前检查并补全 serviceProxy 配置(如果 ecweb 启用但 serviceProxy 未配置)
|
|
2638
|
-
*/
|
|
2639
|
-
function ensureServiceProxyConfigBeforeStart(p) {
|
|
2640
|
-
const cfg = loadDaemonConfig();
|
|
2641
|
-
// 仅在 ecweb 启用 + 控制 AID 已配置 + serviceProxy 未配置 ecweb 时自动添加
|
|
2642
|
-
if (!cfg.ecweb?.enabled || !cfg.aid)
|
|
2643
|
-
return;
|
|
2644
|
-
const hasEcwebService = (cfg.serviceProxy?.services ?? []).some(s => s.name === 'ecweb' || s.source === 'ecweb');
|
|
2645
|
-
if (!hasEcwebService) {
|
|
2646
|
-
const updatedCfg = {
|
|
2647
|
-
...cfg,
|
|
2648
|
-
serviceProxy: {
|
|
2649
|
-
enabled: true,
|
|
2650
|
-
...(cfg.serviceProxy ?? {}),
|
|
2651
|
-
services: [
|
|
2652
|
-
...(cfg.serviceProxy?.services ?? []),
|
|
2653
|
-
{
|
|
2654
|
-
name: 'ecweb',
|
|
2655
|
-
source: 'ecweb',
|
|
2656
|
-
serviceType: 'http',
|
|
2657
|
-
visibility: 'public',
|
|
2658
|
-
enabled: true,
|
|
2659
|
-
metadata: {
|
|
2660
|
-
label: 'EvolCore Dashboard',
|
|
2661
|
-
},
|
|
2662
|
-
},
|
|
2663
|
-
],
|
|
2664
|
-
},
|
|
2665
|
-
};
|
|
2666
|
-
saveDaemonConfig(updatedCfg);
|
|
2667
|
-
console.log(`✓ 自动配置 Service Proxy: https://${cfg.aid}/proxy/ecweb/`);
|
|
2668
|
-
}
|
|
2669
|
-
}
|
|
2670
|
-
/**
|
|
2671
|
-
* 确保 serviceProxy 配置包含 ecweb 服务(ECWeb 启动成功后自动配置)
|
|
2672
|
-
*/
|
|
2673
|
-
function ensureServiceProxyConfig(cfg, port) {
|
|
2674
|
-
// 仅在控制 AID 已配置时自动添加 serviceProxy
|
|
2675
|
-
if (!cfg.aid)
|
|
2676
|
-
return;
|
|
2677
|
-
const hasEcwebService = (cfg.serviceProxy?.services ?? []).some(s => s.name === 'ecweb' || s.source === 'ecweb');
|
|
2678
|
-
if (!hasEcwebService) {
|
|
2679
|
-
const updatedCfg = {
|
|
2680
|
-
...cfg,
|
|
2681
|
-
serviceProxy: {
|
|
2682
|
-
enabled: true,
|
|
2683
|
-
...(cfg.serviceProxy ?? {}),
|
|
2684
|
-
services: [
|
|
2685
|
-
...(cfg.serviceProxy?.services ?? []),
|
|
2686
|
-
{
|
|
2687
|
-
name: 'ecweb',
|
|
2688
|
-
source: 'ecweb',
|
|
2689
|
-
serviceType: 'http',
|
|
2690
|
-
visibility: 'public',
|
|
2691
|
-
enabled: true,
|
|
2692
|
-
metadata: {
|
|
2693
|
-
label: 'EvolCore Dashboard',
|
|
2694
|
-
},
|
|
2695
|
-
},
|
|
2696
|
-
],
|
|
2697
|
-
},
|
|
2698
|
-
};
|
|
2699
|
-
saveDaemonConfig(updatedCfg);
|
|
2700
|
-
console.log(` 自动配置 Service Proxy: https://${cfg.aid}/proxy/ecweb/`);
|
|
2701
|
-
}
|
|
2702
|
-
}
|
|
2703
2662
|
/**
|
|
2704
2663
|
* Start the separately installed `ec-web` process for a configured runtime.
|
|
2705
2664
|
* Kept public for restart-monitor, which launches the daemon directly and
|
|
@@ -2707,7 +2666,8 @@ function ensureServiceProxyConfig(cfg, port) {
|
|
|
2707
2666
|
*/
|
|
2708
2667
|
export async function startEcwebIfEnabled(p, options = {}) {
|
|
2709
2668
|
const cfg = loadDaemonConfig();
|
|
2710
|
-
|
|
2669
|
+
const service = ecwebService(cfg.services);
|
|
2670
|
+
if (!service?.enabled)
|
|
2711
2671
|
return false;
|
|
2712
2672
|
// The daemon is the lifecycle owner. CLI callers normally only need to
|
|
2713
2673
|
// ensure ECWeb is available, so adopt a healthy process instead of killing
|
|
@@ -2744,7 +2704,7 @@ export async function startEcwebIfEnabled(p, options = {}) {
|
|
|
2744
2704
|
console.log(`❌ EC Web 旧进程未清理干净,取消启动: ${error instanceof Error ? error.message : String(error)}`);
|
|
2745
2705
|
return false;
|
|
2746
2706
|
}
|
|
2747
|
-
const port =
|
|
2707
|
+
const port = service.port ?? DEFAULT_ECWEB_PORT;
|
|
2748
2708
|
const args = ['--home', p.root, '--port', String(port)];
|
|
2749
2709
|
const launch = resolveEcwebLaunchCommand(args, { installedPkg });
|
|
2750
2710
|
if (!launch) {
|
|
@@ -2874,10 +2834,9 @@ async function cmdWatchWeb() {
|
|
|
2874
2834
|
}
|
|
2875
2835
|
// 2. 启动(后台)并同步配置。默认行为是替换旧实例,确保新配置/新版静态资源生效。
|
|
2876
2836
|
const cfg = loadDaemonConfig();
|
|
2877
|
-
const
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
ensureServiceProxyConfigBeforeStart(p);
|
|
2837
|
+
const currentService = ecwebService(cfg.services);
|
|
2838
|
+
const port = currentService?.port ?? DEFAULT_ECWEB_PORT;
|
|
2839
|
+
saveDaemonConfig({ ...cfg, services: setEcwebEnabled(cfg.services, true, port) });
|
|
2881
2840
|
const ok = await startEcwebIfEnabled(p, { forceRestart: true });
|
|
2882
2841
|
if (!ok)
|
|
2883
2842
|
process.exit(1); // 失败原因已由 startEcwebIfEnabled 打印
|
|
@@ -3005,8 +2964,9 @@ export async function cmdDiagnose() {
|
|
|
3005
2964
|
// 6. 检查 EC Web 端口与实例记录是否一致。
|
|
3006
2965
|
try {
|
|
3007
2966
|
const cfg = loadDaemonConfig();
|
|
3008
|
-
|
|
3009
|
-
|
|
2967
|
+
const service = ecwebService(cfg.services);
|
|
2968
|
+
if (service?.enabled) {
|
|
2969
|
+
const port = service.port ?? DEFAULT_ECWEB_PORT;
|
|
3010
2970
|
const portPids = platform.findProcessByPort(port);
|
|
3011
2971
|
const recordedPids = new Set(readEcwebInstanceEntries(p).map(entry => entry.record.pid));
|
|
3012
2972
|
const unregisteredPids = portPids.filter(pid => !recordedPids.has(pid));
|
|
@@ -3077,7 +3037,7 @@ export async function cmdWatchCommand(args) {
|
|
|
3077
3037
|
if (requested.length > 0) {
|
|
3078
3038
|
const p2 = resolvePaths();
|
|
3079
3039
|
const avail = fs.existsSync(p2.logs)
|
|
3080
|
-
? deriveLogTypes(fs.readdirSync(p2.logs).filter(
|
|
3040
|
+
? deriveLogTypes(fs.readdirSync(p2.logs).filter(isWatchLogFile))
|
|
3081
3041
|
: [];
|
|
3082
3042
|
const invalid = validateLogTypes(requested, avail);
|
|
3083
3043
|
if (invalid.length > 0) {
|
package/dist/cli/index.js
CHANGED
|
@@ -118,6 +118,7 @@ export async function main(args) {
|
|
|
118
118
|
|
|
119
119
|
仅初始化 defaults.json:
|
|
120
120
|
ec init 交互式(写完 defaults.json 后嵌套 agent new)
|
|
121
|
+
选择 ecagent 时会继续询问 API Key 和 Base URL
|
|
121
122
|
ec init --non-interactive [选项]
|
|
122
123
|
--baseagent <claude|codex|gemini|ecagent> 默认: PATH 中第一个可用项;ecagent 需显式选择
|
|
123
124
|
--force 已存在 defaults.json 时覆盖
|
package/dist/cli/init.js
CHANGED
|
@@ -6,9 +6,10 @@ 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 { ecwebService, setEcwebEnabled } from '../config/daemon-services.js';
|
|
9
10
|
import { generateControlAid, resolveControlAidDomain } from '../aun/aid/control-aid.js';
|
|
10
11
|
import { getCodexAppServerAvailability } from '../agents/codex-runner.js';
|
|
11
|
-
import { resolveEcagentConfig } from '../agents/baseagent.js';
|
|
12
|
+
import { DEFAULT_ECAGENT_BASE_URL, resolveEcagentConfig } from '../agents/baseagent.js';
|
|
12
13
|
import { defaultProjectsRoot } from '../utils/project-path.js';
|
|
13
14
|
import { WEB_CLI_BIN, WEB_PACKAGE_LATEST } from '../product.js';
|
|
14
15
|
import { autostartInstalled, autostartPlatformLabel, configureAutostart } from '../utils/autostart.js';
|
|
@@ -79,14 +80,14 @@ function ecagentUnavailableReason() {
|
|
|
79
80
|
return error instanceof Error ? error.message : String(error);
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
|
-
function buildDefaults(chosen, available, projectsDefaultPath) {
|
|
83
|
+
function buildDefaults(chosen, available, projectsDefaultPath, ecagentConfig) {
|
|
83
84
|
const baseagents = {};
|
|
84
85
|
for (const b of available) {
|
|
85
86
|
baseagents[b] = {};
|
|
86
87
|
}
|
|
87
88
|
// ecagent is bundled with EvolCore, so declare it even when the caller
|
|
88
89
|
// supplies a reduced availability list (for example, a test probe).
|
|
89
|
-
baseagents.ecagent
|
|
90
|
+
baseagents.ecagent = ecagentConfig ?? baseagents.ecagent ?? {};
|
|
90
91
|
return {
|
|
91
92
|
$schema_version: 1,
|
|
92
93
|
active_baseagent: chosen,
|
|
@@ -94,8 +95,8 @@ function buildDefaults(chosen, available, projectsDefaultPath) {
|
|
|
94
95
|
...(projectsDefaultPath ? { projects: { defaultPath: projectsDefaultPath } } : {}),
|
|
95
96
|
};
|
|
96
97
|
}
|
|
97
|
-
function writeDefaults(chosen, available, projectsDefaultPath) {
|
|
98
|
-
saveDefaultsSafe(buildDefaults(chosen, available, projectsDefaultPath));
|
|
98
|
+
function writeDefaults(chosen, available, projectsDefaultPath, ecagentConfig) {
|
|
99
|
+
saveDefaultsSafe(buildDefaults(chosen, available, projectsDefaultPath, ecagentConfig));
|
|
99
100
|
}
|
|
100
101
|
function hasCompletedInitConfig() {
|
|
101
102
|
const p = resolvePaths();
|
|
@@ -263,7 +264,7 @@ export async function cmdInit(options) {
|
|
|
263
264
|
}
|
|
264
265
|
}
|
|
265
266
|
const available = detectAvailable(codexAvailability);
|
|
266
|
-
if (available.length === 0
|
|
267
|
+
if (available.length === 0) {
|
|
267
268
|
console.log('❌ 未检测到可用 baseagent。请安装至少一款:');
|
|
268
269
|
console.log(' - claude CLI');
|
|
269
270
|
console.log(' - gemini CLI');
|
|
@@ -361,13 +362,6 @@ export async function cmdInit(options) {
|
|
|
361
362
|
console.log(` ${input} 当前环境不可用(可用: ${available.join('/')})`);
|
|
362
363
|
continue;
|
|
363
364
|
}
|
|
364
|
-
if (input === 'ecagent') {
|
|
365
|
-
const reason = ecagentUnavailableReason();
|
|
366
|
-
if (reason) {
|
|
367
|
-
console.log(` ecagent 当前环境不可用:${reason}`);
|
|
368
|
-
continue;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
365
|
chosen = input;
|
|
372
366
|
}
|
|
373
367
|
return chosen;
|
|
@@ -405,13 +399,59 @@ export async function cmdInit(options) {
|
|
|
405
399
|
return resolved;
|
|
406
400
|
}
|
|
407
401
|
}
|
|
402
|
+
async function askEcagentConfig() {
|
|
403
|
+
let configured;
|
|
404
|
+
try {
|
|
405
|
+
const raw = JSON.parse(fs.readFileSync(defaultsPath, 'utf8'));
|
|
406
|
+
const candidate = raw?.baseagents?.ecagent;
|
|
407
|
+
if (candidate && typeof candidate === 'object' && !Array.isArray(candidate)) {
|
|
408
|
+
configured = candidate;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
catch {
|
|
412
|
+
// A malformed or missing defaults file should not prevent the user
|
|
413
|
+
// from supplying a fresh ecagent configuration during init.
|
|
414
|
+
configured = undefined;
|
|
415
|
+
}
|
|
416
|
+
const usable = (value) => {
|
|
417
|
+
if (typeof value !== 'string')
|
|
418
|
+
return false;
|
|
419
|
+
const normalized = value.trim();
|
|
420
|
+
if (!normalized)
|
|
421
|
+
return false;
|
|
422
|
+
const lower = normalized.toLowerCase();
|
|
423
|
+
return !lower.includes('your-') && !lower.includes('placeholder');
|
|
424
|
+
};
|
|
425
|
+
const configuredBaseUrl = [
|
|
426
|
+
configured?.baseUrl,
|
|
427
|
+
process.env.ECAGENT_BASE_URL,
|
|
428
|
+
DEFAULT_ECAGENT_BASE_URL,
|
|
429
|
+
].find(usable) || DEFAULT_ECAGENT_BASE_URL;
|
|
430
|
+
const baseUrl = (await ask(rl, `ecagent Base URL [${configuredBaseUrl}]: `)).trim() || configuredBaseUrl;
|
|
431
|
+
const configuredApiKey = usable(configured?.apiKey) ? configured.apiKey.trim() : undefined;
|
|
432
|
+
const environmentApiKeyName = !configuredApiKey
|
|
433
|
+
? (usable(process.env.ECAGENT_API_KEY)
|
|
434
|
+
? 'ECAGENT_API_KEY'
|
|
435
|
+
: usable(process.env.OPENAI_API_KEY) ? 'OPENAI_API_KEY' : undefined)
|
|
436
|
+
: undefined;
|
|
437
|
+
let apiKey = '';
|
|
438
|
+
while (!apiKey) {
|
|
439
|
+
const suffix = configuredApiKey || environmentApiKeyName ? '(回车保留当前配置)' : '(必填)';
|
|
440
|
+
const input = (await ask(rl, `ecagent API Key ${suffix}: `)).trim();
|
|
441
|
+
apiKey = input || configuredApiKey || (environmentApiKeyName ? '${' + environmentApiKeyName + '}' : '');
|
|
442
|
+
if (!apiKey)
|
|
443
|
+
console.log(' API Key 不能为空,请重新输入');
|
|
444
|
+
}
|
|
445
|
+
return { apiKey, baseUrl };
|
|
446
|
+
}
|
|
408
447
|
try {
|
|
409
448
|
if (defaultsExisted) {
|
|
410
449
|
const ans = (await ask(rl, `配置文件已存在: ${defaultsPath}\n 是否覆盖?[y/N] `)).trim().toLowerCase();
|
|
411
450
|
if (ans === 'y' || ans === 'yes') {
|
|
412
451
|
const chosen = await askBaseagent();
|
|
452
|
+
const ecagentConfig = chosen === 'ecagent' ? await askEcagentConfig() : undefined;
|
|
413
453
|
const projectsDefaultPath = await askProjectsDefaultPath();
|
|
414
|
-
writeDefaults(chosen, available, projectsDefaultPath);
|
|
454
|
+
writeDefaults(chosen, available, projectsDefaultPath, ecagentConfig);
|
|
415
455
|
console.log(`\n✓ 已覆盖: ${defaultsPath}`);
|
|
416
456
|
console.log(` active_baseagent: ${chosen}\n`);
|
|
417
457
|
}
|
|
@@ -421,8 +461,9 @@ export async function cmdInit(options) {
|
|
|
421
461
|
}
|
|
422
462
|
else {
|
|
423
463
|
const chosen = await askBaseagent();
|
|
464
|
+
const ecagentConfig = chosen === 'ecagent' ? await askEcagentConfig() : undefined;
|
|
424
465
|
const projectsDefaultPath = await askProjectsDefaultPath();
|
|
425
|
-
writeDefaults(chosen, available, projectsDefaultPath);
|
|
466
|
+
writeDefaults(chosen, available, projectsDefaultPath, ecagentConfig);
|
|
426
467
|
console.log(`\n✓ 已创建: ${defaultsPath}`);
|
|
427
468
|
console.log(` active_baseagent: ${chosen}\n`);
|
|
428
469
|
}
|
|
@@ -578,25 +619,34 @@ export async function initTail(options = {}) {
|
|
|
578
619
|
}
|
|
579
620
|
async function handleEcwebPrompt(rl) {
|
|
580
621
|
const daemonConfigForEcweb = loadDaemonConfig();
|
|
581
|
-
if (daemonConfigForEcweb.
|
|
622
|
+
if (ecwebService(daemonConfigForEcweb.services)?.enabled === undefined) {
|
|
582
623
|
const ans = (await ask(rl, '\n是否在 ec start 时自动启动 ECWeb 控制台?[y/N] ')).trim().toLowerCase();
|
|
583
624
|
if (ans === 'y' || ans === 'yes') {
|
|
584
625
|
const installResult = await ensureEcwebInstalledForInit();
|
|
585
626
|
if (installResult.ok) {
|
|
586
627
|
const cfg = loadDaemonConfig();
|
|
587
|
-
saveDaemonConfig({
|
|
628
|
+
saveDaemonConfig({
|
|
629
|
+
...cfg,
|
|
630
|
+
services: setEcwebEnabled(cfg.services, true),
|
|
631
|
+
});
|
|
588
632
|
console.log(' ✓ 已启用 ECWeb(ec start 将自动在后台启动)');
|
|
589
633
|
console.log(' 提示:首次访问运行 ec watch web 查看配对码和 URL');
|
|
590
634
|
}
|
|
591
635
|
else {
|
|
592
636
|
const cfg = loadDaemonConfig();
|
|
593
|
-
saveDaemonConfig({
|
|
637
|
+
saveDaemonConfig({
|
|
638
|
+
...cfg,
|
|
639
|
+
services: setEcwebEnabled(cfg.services, false),
|
|
640
|
+
});
|
|
594
641
|
console.log(' 提示:安装完成后运行 ec start;如需立即打开控制台,运行 ec watch web');
|
|
595
642
|
}
|
|
596
643
|
}
|
|
597
644
|
else {
|
|
598
645
|
const cfg = loadDaemonConfig();
|
|
599
|
-
saveDaemonConfig({
|
|
646
|
+
saveDaemonConfig({
|
|
647
|
+
...cfg,
|
|
648
|
+
services: setEcwebEnabled(cfg.services, false),
|
|
649
|
+
});
|
|
600
650
|
console.log(' 已跳过(可日后运行 ec watch web 手动启动,或编辑 daemon.json)');
|
|
601
651
|
}
|
|
602
652
|
}
|
|
@@ -618,7 +668,7 @@ export async function initTail(options = {}) {
|
|
|
618
668
|
const finalCfg = loadDaemonConfig();
|
|
619
669
|
console.log(` 控制 AID: ${finalCfg.aid || '(未配置)'}`);
|
|
620
670
|
console.log(` 管理者: ${finalCfg.owners?.join(', ') || '(未配置)'}`);
|
|
621
|
-
console.log(` ECWeb 自启动: ${finalCfg.
|
|
671
|
+
console.log(` ECWeb 自启动: ${ecwebService(finalCfg.services)?.enabled ? '已启用' : '已禁用'}`);
|
|
622
672
|
const { agents: finalAgents } = loadAllAgents();
|
|
623
673
|
if (finalAgents.length === 0) {
|
|
624
674
|
console.log('\n📌 下一步:创建 agent');
|
|
@@ -786,11 +836,11 @@ export async function cmdInitNonInteractive(opts, dependencies = {}) {
|
|
|
786
836
|
const ensureEcweb = dependencies.ensureEcweb ?? (() => ensureEcwebInstalledForInit({ quiet: true }));
|
|
787
837
|
const installResult = await ensureEcweb();
|
|
788
838
|
if (!installResult.ok) {
|
|
789
|
-
if (existingCfg.
|
|
839
|
+
if (ecwebService(existingCfg.services)?.enabled === true) {
|
|
790
840
|
try {
|
|
791
841
|
saveDaemonConfig({
|
|
792
842
|
...existingCfg,
|
|
793
|
-
|
|
843
|
+
services: setEcwebEnabled(existingCfg.services, false),
|
|
794
844
|
});
|
|
795
845
|
}
|
|
796
846
|
catch (e) {
|
|
@@ -826,7 +876,9 @@ export async function cmdInitNonInteractive(opts, dependencies = {}) {
|
|
|
826
876
|
$schema_version: existingCfg.$schema_version ?? 1,
|
|
827
877
|
aid: controlAid,
|
|
828
878
|
owners: [ownerAid],
|
|
829
|
-
...(opts.ecweb === true
|
|
879
|
+
...(opts.ecweb === true
|
|
880
|
+
? { services: setEcwebEnabled(existingCfg.services, true) }
|
|
881
|
+
: {}),
|
|
830
882
|
};
|
|
831
883
|
saveDaemonConfig(next);
|
|
832
884
|
}
|
|
@@ -854,7 +906,7 @@ export async function cmdInitNonInteractive(opts, dependencies = {}) {
|
|
|
854
906
|
controlAid: controlAid,
|
|
855
907
|
ownerAid,
|
|
856
908
|
owners: [ownerAid],
|
|
857
|
-
ecwebEnabled: opts.ecweb === true || existingCfg.
|
|
909
|
+
ecwebEnabled: opts.ecweb === true || ecwebService(existingCfg.services)?.enabled === true,
|
|
858
910
|
baseagent: chosenBaseagent,
|
|
859
911
|
projectsDefaultPath: projectsDefaultPath ?? null,
|
|
860
912
|
defaultsPath: p.defaultsConfig,
|
|
@@ -14,7 +14,7 @@ import { WEB_PACKAGE_NAME } from '../product.js';
|
|
|
14
14
|
import { shouldSuppressRealRestart } from '../utils/restart-safety.js';
|
|
15
15
|
import { rotateStdoutLog } from '../utils/log-writer.js';
|
|
16
16
|
import { inspectDataMigrationRequirement } from '../core/data-migration.js';
|
|
17
|
-
import { requestDaemonShutdown } from './daemon-commands.js';
|
|
17
|
+
import { DAEMON_SHUTDOWN_TIMEOUT_MS, requestDaemonShutdown } from './daemon-commands.js';
|
|
18
18
|
const execFileAsync = promisify(execFile);
|
|
19
19
|
// 清理 Claude Code 环境变量,防止 SDK 认为是嵌套会话
|
|
20
20
|
function cleanEnv() {
|
|
@@ -107,7 +107,7 @@ export async function cmdRestartMonitor() {
|
|
|
107
107
|
const pids = runtimeOrphans.map(o => o.pid).join(', ');
|
|
108
108
|
log(`Stopping unregistered EvolCore process(es): ${pids}`);
|
|
109
109
|
const orphanPids = new Set(runtimeOrphans.map(orphan => orphan.pid));
|
|
110
|
-
const ping = await requestDaemonShutdown(p.socket,
|
|
110
|
+
const ping = await requestDaemonShutdown(p.socket, DAEMON_SHUTDOWN_TIMEOUT_MS, 'restart-monitor orphan cleanup', orphanPids);
|
|
111
111
|
for (const orphan of runtimeOrphans) {
|
|
112
112
|
if (orphan.pid !== ping)
|
|
113
113
|
platform.killProcess(orphan.pid, false);
|
|
@@ -134,7 +134,7 @@ export async function cmdRestartMonitor() {
|
|
|
134
134
|
if (aliveMains.length > 0) {
|
|
135
135
|
const oldPids = aliveMains.map(m => m.record.pid);
|
|
136
136
|
log(`Monitoring ${oldPids.length} main process(es): ${oldPids.join(', ')}`);
|
|
137
|
-
const gracefulPid = await requestDaemonShutdown(p.socket,
|
|
137
|
+
const gracefulPid = await requestDaemonShutdown(p.socket, DAEMON_SHUTDOWN_TIMEOUT_MS, 'restart-monitor', new Set(oldPids));
|
|
138
138
|
// 没有收到 IPC shutdown 确认的进程才走信号兜底
|
|
139
139
|
for (const pid of oldPids) {
|
|
140
140
|
if (pid !== gracefulPid) {
|