evolcore 0.0.35 → 0.0.37
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 +42 -0
- package/README.md +6 -0
- package/bin/install-codex-managed-hooks.mjs +20 -3
- package/dist/agents/baseagent-availability.js +1 -1
- package/dist/agents/baseagent.js +88 -0
- package/dist/agents/claude-runner.js +59 -12
- package/dist/agents/codex/app-server-client.js +18 -1
- package/dist/agents/codex-runner.js +262 -21
- package/dist/agents/ecagent-runner.js +47 -11
- package/dist/agents/gemini-runner.js +36 -12
- package/dist/aun/aid/client.js +4 -3
- package/dist/aun/aid/control-aid.js +270 -8
- package/dist/aun/aid/identity.js +6 -3
- package/dist/aun/aid/ownership-operation.js +58 -0
- package/dist/aun/aid/store.js +1 -0
- package/dist/aun/group/fs-download.js +67 -2
- package/dist/aun/group/history.js +20 -5
- package/dist/aun/group/operations.js +8 -2
- package/dist/aun/menu/client.js +39 -2
- package/dist/aun/menu/transport.js +1 -1
- package/dist/aun/msg/history.js +43 -4
- package/dist/aun/msg/managed-operation.js +30 -53
- package/dist/aun/msg/p2p.js +51 -15
- package/dist/aun/outbox.js +4 -0
- package/dist/channels/aun.js +260 -73
- package/dist/channels/daemon.js +6 -4
- package/dist/channels/feishu.js +3 -0
- package/dist/cli/agent-command.js +161 -223
- package/dist/cli/agent-create.js +190 -223
- package/dist/cli/agent.js +3 -1
- package/dist/cli/aid-bind-command.js +53 -0
- package/dist/cli/aun-commands.js +384 -91
- package/dist/cli/bench.js +1 -1
- package/dist/cli/cli-argv.js +74 -0
- package/dist/cli/daemon-commands.js +105 -74
- package/dist/cli/diag.js +1 -1
- package/dist/cli/fs-command.js +18 -19
- package/dist/cli/getmeta-command.js +159 -6
- package/dist/cli/index.js +6 -12
- package/dist/cli/init-channel.js +8 -55
- package/dist/cli/init.js +125 -218
- package/dist/cli/queue-command.js +11 -8
- package/dist/cli/restart-monitor.js +177 -85
- package/dist/cli/session-command.js +1 -1
- package/dist/cli/stats.js +7 -5
- package/dist/cli/thread-command.js +103 -30
- package/dist/cli/trigger-command.js +1 -1
- package/dist/cli/watch-msg-diag.js +1 -0
- package/dist/config/access-policy.js +2 -2
- package/dist/config/boot-log.js +14 -11
- package/dist/config/config-field-policy.js +9 -1
- package/dist/config/config-manager.js +176 -90
- package/dist/config/config-operation-service.js +7 -14
- package/dist/config/contact-book-store.js +142 -26
- package/dist/config/contact-book.js +4 -2
- package/dist/config/lifecycle.js +1 -1
- package/dist/config/owner-config.js +96 -0
- package/dist/config/peer-role-resolver.js +12 -18
- package/dist/config/providers-config.js +382 -0
- package/dist/config/providers-import.js +74 -0
- package/dist/config/providers-management.js +328 -0
- package/dist/config/providers.js +22 -0
- package/dist/config/resolved-config-op.js +17 -14
- package/dist/config/snapshot.js +28 -16
- package/dist/config-store.js +18 -14
- package/dist/core/agent-ipc-registration.js +21 -63
- package/dist/core/agent-runtime.js +16 -5
- package/dist/core/auth/agent-delegation.js +199 -25
- package/dist/core/auth/auth-gateway.js +44 -2
- package/dist/core/auth/authorization-denial.js +1 -1
- package/dist/core/auth/debug-context.js +2 -1
- package/dist/core/auth/delegation-authorization.js +2 -2
- package/dist/core/auth/delegation-diagnostics.js +4 -1
- package/dist/core/auth/operation-authorizer.js +10 -1
- package/dist/core/auth/operation-catalog.js +19 -1
- package/dist/core/bootstrap-service.js +43 -20
- package/dist/core/channel-loader.js +0 -1
- package/dist/core/channel-runtime.js +30 -4
- package/dist/core/command/agent-control.js +196 -37
- package/dist/core/command/cli-intent-parser.js +60 -4
- package/dist/core/command/command-handler.js +212 -60
- package/dist/core/command/connect-menu.js +1 -0
- package/dist/core/command/diag-service.js +5 -4
- package/dist/core/command/file-search.js +368 -0
- package/dist/core/command/menu-catalog.js +2 -1
- package/dist/core/command/menu-handler.js +292 -107
- package/dist/core/command/menu-protocol.js +23 -0
- package/dist/core/command/role-menu.js +7 -19
- package/dist/core/command/skill-slash.js +130 -25
- package/dist/core/command/slash-handler.js +101 -53
- package/dist/core/command/topic-slash.js +16 -5
- package/dist/core/daemon-bootstrap.js +7 -1
- package/dist/core/daemon-control.js +19 -1
- package/dist/core/daemon-lifecycle.js +2 -1
- package/dist/core/event-catalog.js +2 -0
- package/dist/core/evolagent-registry.js +13 -2
- package/dist/core/evolagent.js +3 -6
- package/dist/core/handoff/ipc-registration.js +2 -2
- package/dist/core/handoff/runtime.js +40 -6
- package/dist/core/handoff/store.js +1 -0
- package/dist/core/inference/text-inference.js +95 -6
- package/dist/core/ipc-registration.js +43 -24
- package/dist/core/memory/consolidation-context.js +4 -1
- package/dist/core/memory/consolidation.js +25 -14
- package/dist/core/message/aun-ipc-sender.js +296 -88
- package/dist/core/message/aun-request-registry.js +226 -0
- package/dist/core/message/create-status.js +33 -4
- package/dist/core/message/message-bridge.js +62 -13
- package/dist/core/message/message-log.js +0 -1
- package/dist/core/message/message-queue.js +4 -1
- package/dist/core/message/message-utils.js +8 -1
- package/dist/core/message/peer-mode.js +17 -9
- package/dist/core/message/response-engine.js +266 -124
- package/dist/core/message/retry-scheduler.js +11 -8
- package/dist/core/model/catalog-service.js +60 -18
- package/dist/core/model/model-catalog.js +112 -9
- package/dist/core/model/model-fallback.js +94 -26
- package/dist/core/ownership/owner-resolver.js +687 -0
- package/dist/core/permission/approval-gateway.js +196 -62
- package/dist/core/permission/ec-command-parser.js +138 -25
- package/dist/core/permission/filesystem-approval-scope.js +141 -0
- package/dist/core/permission/protected-paths.js +14 -1
- package/dist/core/permission/sandbox-runtime.js +171 -18
- package/dist/core/permission/tool-policy.js +13 -2
- package/dist/core/role/runtime-policy.js +3 -4
- package/dist/core/session/session-idle-monitor.js +10 -8
- package/dist/core/session/session-manager.js +72 -45
- package/dist/core/session/session-mapper.js +18 -7
- package/dist/core/session/session-renew.js +5 -4
- package/dist/core/session/session-title.js +2 -1
- package/dist/core/session/session-turn-coordinator.js +4 -2
- package/dist/core/skill/index-generator.js +2 -16
- package/dist/core/task/task-recovery-service.js +15 -2
- package/dist/core/thread/command-parser.js +28 -13
- package/dist/core/thread/index.js +1 -0
- package/dist/core/thread/ipc-operation.js +11 -8
- package/dist/core/thread/thread-operation.js +79 -44
- package/dist/core/thread/thread-route.js +54 -6
- package/dist/core/thread/thread-service.js +236 -45
- package/dist/core/thread/topic-id.js +248 -0
- package/dist/eck/kit-renderer.js +1 -1
- package/dist/eck/message-renderer.js +12 -2
- package/dist/index.js +50 -27
- package/dist/ipc.js +59 -6
- package/dist/paths.js +1 -0
- package/dist/product.js +1 -0
- package/dist/response-system/engines/v1/proactive-flow.js +10 -3
- package/dist/stats/db.js +8 -0
- package/dist/stats/writer.js +26 -4
- package/dist/trigger/authorization.js +48 -3
- package/dist/trigger/feedback.js +28 -10
- package/dist/trigger/ipc-service.js +86 -10
- package/dist/trigger/local-files.js +55 -23
- package/dist/trigger/scheduler.js +57 -11
- package/dist/trigger/system-definitions.js +1 -0
- package/dist/trigger/validation.js +44 -15
- package/dist/utils/aid-bind.js +17 -297
- package/dist/utils/avatar-upload.js +16 -4
- package/dist/utils/cross-platform.js +115 -36
- package/dist/utils/ecweb-supervisor.js +1 -1
- package/dist/utils/ecweb-utils.js +8 -1
- package/dist/utils/error-utils.js +44 -6
- package/dist/utils/instance-registry.js +12 -2
- package/dist/utils/npm-ops.js +102 -27
- package/dist/utils/package-version-info.js +72 -27
- package/dist/utils/restart-safety.js +50 -0
- package/dist/utils/upgrade-jobs.js +304 -0
- package/dist/utils/windows-autostart.js +17 -10
- package/kits/docs/INDEX.md +5 -0
- package/kits/docs/aun/ABILITY_CALLING.md +239 -0
- package/kits/docs/evolcore/INDEX.md +14 -3
- package/kits/docs/evolcore/agent-create.md +55 -141
- package/kits/docs/evolcore/agent.md +38 -16
- package/kits/docs/evolcore/aid.md +34 -0
- package/kits/docs/evolcore/config.md +20 -1
- package/kits/docs/evolcore/event.md +2 -2
- package/kits/docs/evolcore/fs.md +18 -2
- package/kits/docs/evolcore/group-collaboration.md +13 -246
- package/kits/docs/evolcore/group.md +2 -1
- package/kits/docs/evolcore/memory-consolidation-batches.md +1 -1
- package/kits/docs/evolcore/menu-protocol.md +65 -1
- package/kits/docs/evolcore/msg.md +64 -2
- package/kits/docs/evolcore/skill.md +86 -0
- package/kits/docs/evolcore/stats.md +6 -0
- package/kits/docs/evolcore/task.md +2 -0
- package/kits/docs/evolcore/thread-chain.md +145 -51
- package/kits/docs/evolcore/trigger.md +48 -3
- package/kits/eck_manifest.json +21 -0
- package/kits/eck_message_manifest.json +4 -4
- package/kits/migrations/migrate-role-config-v5.mjs +20 -7
- package/kits/rules/01-overview.md +2 -1
- package/kits/rules/03-identity.md +3 -1
- package/kits/rules/04-relation.md +1 -1
- package/kits/rules/07-memory-and-thread.md +1 -1
- package/kits/schemas/_meta.json +16 -1
- package/kits/schemas/agent-config.schema.13.json +44 -2
- package/kits/schemas/contact-book.schema.3.json +5 -0
- package/kits/schemas/daemon.schema.6.json +3 -1
- package/kits/schemas/daemon.schema.8.json +329 -0
- package/kits/schemas/defaults.schema.5.json +39 -3
- package/kits/schemas/providers.schema.1.json +76 -0
- package/kits/schemas/relation-config.schema.9.json +5 -1
- package/kits/skills/.switches.json +28 -0
- package/kits/skills/aid-ability/SKILL.md +214 -0
- package/kits/skills/ec-agent-create/SKILL.md +162 -0
- package/kits/skills/ec-group/SKILL.md +2 -1
- package/kits/skills/ec-group-collaboration/SKILL.md +228 -0
- package/kits/skills/ec-identity/SKILL.md +143 -0
- package/kits/skills/ec-skill/scripts/cli.mjs +36 -3
- package/kits/skills/ec-skill/scripts/skill.mjs +202 -15
- package/kits/skills/ec-trigger/SKILL.md +167 -0
- package/kits/skills/ec-trigger/references/examples.md +153 -0
- package/kits/skills/thread-ops/SKILL.md +108 -420
- package/kits/templates/message-fragments/thread-context.md +1 -2
- package/kits/templates/message-fragments/trigger-thread-check.md +1 -1
- package/kits/templates/roles/member.json +1 -1
- package/kits/templates/roles/visitor.json +1 -0
- package/kits/templates/system-fragments/bootstrap.md +1 -0
- package/kits/templates/system-fragments/channel.md +2 -1
- package/kits/templates/system-fragments/commands.md +5 -4
- package/kits/templates/system-fragments/identity.md +2 -0
- package/kits/templates/system-fragments/session.md +1 -1
- package/kits/templates/system-fragments/venue.md +1 -1
- package/package.json +4 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
+
import { ipcQuery } from '../ipc.js';
|
|
2
3
|
import path from 'path';
|
|
3
4
|
import { spawn, execFile } from 'child_process';
|
|
4
5
|
import { promisify } from 'util';
|
|
@@ -7,11 +8,12 @@ import { isRestartRecoveryEnabled, loadDaemonConfig } from '../config-store.js';
|
|
|
7
8
|
import * as platform from '../utils/cross-platform.js';
|
|
8
9
|
import { EventBus } from '../core/event-bus.js';
|
|
9
10
|
import { applyCachedPackageUpgrades, recordPackageUpgrade } from '../utils/package-version-info.js';
|
|
11
|
+
import { acknowledgeUpgradeRestart, readUpgradeJob, updateUpgradeJob } from '../utils/upgrade-jobs.js';
|
|
10
12
|
import { isValidAid } from '../aun/aid/index.js';
|
|
11
13
|
import { msgSend } from '../aun/msg/index.js';
|
|
12
|
-
import { scanInstances, cleanupInstances, writeRestartMonitor, removeRestartMonitor, isRestartMonitorWinner, findOrphanProcesses, killOrphans } from '../utils/instance-registry.js';
|
|
13
|
-
import { MAIN_PACKAGE_NAME,
|
|
14
|
-
import { shouldSuppressRealRestart } from '../utils/restart-safety.js';
|
|
14
|
+
import { scanInstances, cleanupInstances, writeRestartMonitor, removeRestartMonitor, isRestartMonitorWinner, findOrphanProcesses, killOrphans, updateRestartMonitorPhase } from '../utils/instance-registry.js';
|
|
15
|
+
import { MAIN_PACKAGE_NAME, RESTART_REQUEST_ID_ENV } from '../product.js';
|
|
16
|
+
import { readRestartRequest, updateRestartRequest, removeRestartRequest, shouldSuppressRealRestart } from '../utils/restart-safety.js';
|
|
15
17
|
import { rotateStdoutLog } from '../utils/log-writer.js';
|
|
16
18
|
import { applyDataMigration, finalizeDataMigration, planDataMigration, verifyDataMigration, } from '../core/data-migration.js';
|
|
17
19
|
import { DAEMON_SHUTDOWN_TIMEOUT_MS, requestDaemonShutdown, stopCodexAppServerOrphans } from './daemon-commands.js';
|
|
@@ -112,6 +114,8 @@ export async function cmdRestartMonitor() {
|
|
|
112
114
|
return;
|
|
113
115
|
const p = resolvePaths();
|
|
114
116
|
const restartLog = path.join(p.logs, 'restart.log');
|
|
117
|
+
const pendingFile = path.join(daemonControlDir(), 'restart-pending.json');
|
|
118
|
+
const requestId = process.env[RESTART_REQUEST_ID_ENV] || 'unknown';
|
|
115
119
|
const MAX_HEAL_ATTEMPTS = 3;
|
|
116
120
|
const READY_TIMEOUT = 30000; // 30s(AUN sidecar + 外部渠道连接预留)
|
|
117
121
|
const HEAL_TIMEOUT = 30 * 60 * 1000; // 30 分钟,让 claude 自然结束
|
|
@@ -120,9 +124,24 @@ export async function cmdRestartMonitor() {
|
|
|
120
124
|
const _d = new Date();
|
|
121
125
|
const _p = (n) => String(n).padStart(2, '0');
|
|
122
126
|
const ts = `${_d.getFullYear()}-${_p(_d.getMonth() + 1)}-${_p(_d.getDate())} ${_p(_d.getHours())}:${_p(_d.getMinutes())}:${_p(_d.getSeconds())}`;
|
|
123
|
-
const line = `[${ts}] ${msg}\n`;
|
|
127
|
+
const line = `[${ts}] [requestId=${requestId}] ${msg}\n`;
|
|
124
128
|
fs.appendFileSync(restartLog, line);
|
|
125
129
|
};
|
|
130
|
+
const hasCorrelatedRequest = requestId !== 'unknown';
|
|
131
|
+
const upgradeJobId = hasCorrelatedRequest ? readRestartRequest(pendingFile, requestId)?.upgradeJobId : undefined;
|
|
132
|
+
const upgradeJob = typeof upgradeJobId === 'string' ? readUpgradeJob(upgradeJobId) : null;
|
|
133
|
+
if (upgradeJobId && (!upgradeJob || upgradeJob.restartRequestId !== requestId || upgradeJob.released || upgradeJob.deadline <= Date.now())) {
|
|
134
|
+
if (upgradeJob)
|
|
135
|
+
updateUpgradeJob(upgradeJob.jobId, { status: 'timeout', success: false, released: true, code: 'UPGRADE_TIMEOUT', reason: 'Update expired before restart preflight' }, '更新已超时,未执行重启');
|
|
136
|
+
process.exitCode = 2;
|
|
137
|
+
cleanupPendingFile(pendingFile, log, requestId);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (hasCorrelatedRequest && !readRestartRequest(pendingFile, requestId)) {
|
|
141
|
+
log('Restart request is missing or has been replaced; refusing to proceed');
|
|
142
|
+
process.exitCode = 2;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
126
145
|
// 单实例保护:pre-check + post-write 自检(同 main 进程)
|
|
127
146
|
{
|
|
128
147
|
const pre = scanInstances();
|
|
@@ -133,8 +152,17 @@ export async function cmdRestartMonitor() {
|
|
|
133
152
|
}
|
|
134
153
|
}
|
|
135
154
|
// 立即登记自己;exit 路径上自动清理 record
|
|
136
|
-
writeRestartMonitor();
|
|
137
|
-
process.on('exit',
|
|
155
|
+
writeRestartMonitor(hasCorrelatedRequest ? requestId : undefined, 'starting');
|
|
156
|
+
process.on('exit', code => {
|
|
157
|
+
if (upgradeJob) {
|
|
158
|
+
const current = readUpgradeJob(upgradeJob.jobId);
|
|
159
|
+
if (current)
|
|
160
|
+
updateUpgradeJob(upgradeJob.jobId, {
|
|
161
|
+
released: true,
|
|
162
|
+
lifecycle: current.lifecycle ?? { status: code === 0 ? 'completed' : 'failed', at: new Date().toISOString(), ...(code !== 0 ? { reason: 'Restart lifecycle failed; inspect restart.log' } : {}) },
|
|
163
|
+
...(['accepted', 'running'].includes(current.status) ? { status: 'failed', success: false, code: 'RESTART_ABORTED', reason: 'Restart monitor exited before entering restart' } : {}),
|
|
164
|
+
}, ['accepted', 'running'].includes(current.status) ? '重启未能开始,可重新发起' : undefined);
|
|
165
|
+
}
|
|
138
166
|
removeRestartMonitor();
|
|
139
167
|
// 安装包模式下本进程跑在 stage 副本里。cwd 不在其中(继承自调用方),
|
|
140
168
|
// 所以可以安全自删;留下来只会占磁盘。
|
|
@@ -162,21 +190,56 @@ export async function cmdRestartMonitor() {
|
|
|
162
190
|
const s = scanInstances();
|
|
163
191
|
return s.mains.some(m => m.alive);
|
|
164
192
|
};
|
|
165
|
-
|
|
193
|
+
const markRequestReady = () => {
|
|
194
|
+
if (!hasCorrelatedRequest)
|
|
195
|
+
return;
|
|
196
|
+
const requesterPid = readRestartRequest(pendingFile, requestId)?.requesterPid;
|
|
197
|
+
const daemonPid = scanInstances().mains.find(item => item.alive && item.record.pid !== requesterPid)?.record.pid;
|
|
198
|
+
if (daemonPid && updateRestartRequest(pendingFile, requestId, { status: 'ready', daemonPid })) {
|
|
199
|
+
if (upgradeJob && !readUpgradeJob(upgradeJob.jobId)?.lifecycle)
|
|
200
|
+
updateUpgradeJob(upgradeJob.jobId, { lifecycle: { status: 'ready', at: new Date().toISOString() } });
|
|
201
|
+
log(`Restart request marked ready (daemonPid=${daemonPid})`);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
log('WARN: could not mark correlated restart request ready');
|
|
205
|
+
}
|
|
206
|
+
};
|
|
166
207
|
// Keep this path available for a failed read-only preflight so a stale
|
|
167
208
|
// restart request cannot be replayed by a later daemon startup.
|
|
168
|
-
|
|
209
|
+
log(`Restart monitor started (requestId=${requestId})`);
|
|
169
210
|
// 升级成功时记下版本变化,回执里要告诉 agent 实际跑的是哪个版本。
|
|
170
211
|
let upgradeVersions;
|
|
212
|
+
if (upgradeJob)
|
|
213
|
+
updateUpgradeJob(upgradeJob.jobId, { phase: 'preflight', monitorPid: process.pid }, '正在执行原有迁移预检');
|
|
171
214
|
const migrationPreflight = preflightDataMigration(p.root, log);
|
|
172
215
|
if (!migrationPreflight.ok) {
|
|
173
216
|
const message = `重启前用户数据迁移预检失败:${migrationPreflight.reason ?? 'unknown error'}`;
|
|
174
217
|
log(message);
|
|
175
218
|
console.error(`❌ ${message}`);
|
|
176
|
-
|
|
219
|
+
if (upgradeJob)
|
|
220
|
+
updateUpgradeJob(upgradeJob.jobId, { status: 'failed', success: false, code: 'PREFLIGHT_FAILED', reason: message }, '迁移预检失败,已中断重启');
|
|
221
|
+
cleanupPendingFile(pendingFile, log, hasCorrelatedRequest ? requestId : undefined);
|
|
177
222
|
process.exitCode = 2;
|
|
178
223
|
return;
|
|
179
224
|
}
|
|
225
|
+
if (hasCorrelatedRequest) {
|
|
226
|
+
if (upgradeJob && (readUpgradeJob(upgradeJob.jobId)?.status !== 'running' || upgradeJob.deadline <= Date.now())) {
|
|
227
|
+
cleanupPendingFile(pendingFile, log, requestId);
|
|
228
|
+
process.exitCode = 2;
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (!updateRestartRequest(pendingFile, requestId, { status: 'accepted', monitorPid: process.pid })) {
|
|
232
|
+
log('Restart request changed before acceptance; refusing to stop the daemon');
|
|
233
|
+
process.exitCode = 2;
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
updateRestartMonitorPhase(process.pid, 'accepted');
|
|
238
|
+
log('Restart monitor accepted after migration preflight and monitor election');
|
|
239
|
+
if (upgradeJob) {
|
|
240
|
+
updateUpgradeJob(upgradeJob.jobId, { status: 'restart-started', success: true, phase: 'restart' }, '预检通过,已进入重启执行;不代表新进程已就绪');
|
|
241
|
+
await acknowledgeUpgradeRestart(upgradeJob.jobId);
|
|
242
|
+
}
|
|
180
243
|
// Test daemons live in one temporary HOME per test, so the current HOME's
|
|
181
244
|
// instance registry cannot see them. Their strict environment markers make
|
|
182
245
|
// them safe to reap globally on every real restart. Keep one process-table
|
|
@@ -281,7 +344,7 @@ export async function cmdRestartMonitor() {
|
|
|
281
344
|
if (exited.some(result => !result)) {
|
|
282
345
|
log('❌ Existing daemon process could not be stopped; refusing to spawn a replacement');
|
|
283
346
|
await notifyDaemonOwners(p, '❌ 旧 EvolCore 进程未能退出,已停止重启,避免重复连接 AUN。', log);
|
|
284
|
-
cleanupPendingFile(pendingFile, log);
|
|
347
|
+
cleanupPendingFile(pendingFile, log, hasCorrelatedRequest ? requestId : undefined);
|
|
285
348
|
process.exit(1);
|
|
286
349
|
}
|
|
287
350
|
// waitForProcessExit above already observed the old daemon exit. A fixed
|
|
@@ -295,7 +358,7 @@ export async function cmdRestartMonitor() {
|
|
|
295
358
|
const remaining = findOrphanProcesses();
|
|
296
359
|
if (scanInstances().mains.some(item => item.alive) || remaining.some(belongsToRuntime)) {
|
|
297
360
|
log('Restart aborted: a runtime process appeared or survived shutdown');
|
|
298
|
-
cleanupPendingFile(pendingFile, log);
|
|
361
|
+
cleanupPendingFile(pendingFile, log, hasCorrelatedRequest ? requestId : undefined);
|
|
299
362
|
process.exit(1);
|
|
300
363
|
}
|
|
301
364
|
const otherHome = remaining.filter(o => !o.confirmedTestDaemon && !belongsToRuntime(o));
|
|
@@ -314,7 +377,10 @@ export async function cmdRestartMonitor() {
|
|
|
314
377
|
// 升级会原地替换整包,先留一个可回退的产物副本。备份放 EVOLCORE_HOME 而非包目录:
|
|
315
378
|
// Linux/macOS 的全局目录常常不可写,而 HOME 一定可写。
|
|
316
379
|
const upgradeBackup = await backupDistForUpgrade(p, log);
|
|
317
|
-
const [upgrade
|
|
380
|
+
const [upgrade] = await applyCachedPackageUpgrades(undefined, {
|
|
381
|
+
log,
|
|
382
|
+
...(upgradeJob ? { target: upgradeJob.target, distTag: upgradeJob.distTag, npmConfig: upgradeJob.npmConfig } : {}),
|
|
383
|
+
});
|
|
318
384
|
let upgradeRolledBack;
|
|
319
385
|
switch (upgrade.status) {
|
|
320
386
|
case 'upgraded':
|
|
@@ -355,19 +421,6 @@ export async function cmdRestartMonitor() {
|
|
|
355
421
|
await notifyDaemonOwners(p, `⚠️ evolcore 升级失败,使用当前版本继续\n${upgrade.error ?? ''}`.trim(), log);
|
|
356
422
|
break;
|
|
357
423
|
}
|
|
358
|
-
// EC Web independently applies a valid cached result under the install lock.
|
|
359
|
-
switch (ecwebUpgrade.status) {
|
|
360
|
-
case 'upgraded':
|
|
361
|
-
log(`✅ ${WEB_PACKAGE_NAME} upgraded: ${ecwebUpgrade.from} → ${ecwebUpgrade.to}`);
|
|
362
|
-
await notifyDaemonOwners(p, `📦 ${WEB_PACKAGE_NAME} 已升级 ${ecwebUpgrade.from} → ${ecwebUpgrade.to}`, log);
|
|
363
|
-
break;
|
|
364
|
-
case 'no-update':
|
|
365
|
-
break;
|
|
366
|
-
case 'failed':
|
|
367
|
-
log(`⚠ ${WEB_PACKAGE_NAME} upgrade failed (${ecwebUpgrade.from} → ${ecwebUpgrade.to}): ${ecwebUpgrade.error}`);
|
|
368
|
-
await notifyDaemonOwners(p, `⚠️ ${WEB_PACKAGE_NAME} 升级失败,使用当前版本继续\n${ecwebUpgrade.error ?? ''}`.trim(), log);
|
|
369
|
-
break;
|
|
370
|
-
}
|
|
371
424
|
// 编译在 daemon 停止之后、拉起之前:此时没有活进程占着 dist/,
|
|
372
425
|
// 失败还能回滚到上一份可用产物,因此这一步整体是可撤销的。
|
|
373
426
|
const buildOutcome = await buildSourcesTransactionally(log);
|
|
@@ -375,7 +428,7 @@ export async function cmdRestartMonitor() {
|
|
|
375
428
|
const message = `服务未重启:${buildOutcome.reason ?? '构建失败'}`;
|
|
376
429
|
log(`❌ ${message}`);
|
|
377
430
|
await notifyDaemonOwners(p, `❌ ${message}`, log);
|
|
378
|
-
markRestartOutcome(pendingFile, 'build-failed', buildOutcome.reason, log);
|
|
431
|
+
markRestartOutcome(pendingFile, 'build-failed', buildOutcome.reason, log, hasCorrelatedRequest ? requestId : undefined);
|
|
379
432
|
process.exit(1);
|
|
380
433
|
}
|
|
381
434
|
if (buildOutcome.status === 'rolled-back') {
|
|
@@ -386,8 +439,8 @@ export async function cmdRestartMonitor() {
|
|
|
386
439
|
const migration = applyPendingDataMigration(p.root, log);
|
|
387
440
|
if (!migration.ok) {
|
|
388
441
|
const message = `服务未重启:用户数据迁移失败(${migration.reason ?? 'unknown error'})`;
|
|
389
|
-
markRestartOutcome(pendingFile, 'migration-failed', migration.reason, log);
|
|
390
|
-
cleanupPendingFile(pendingFile, log);
|
|
442
|
+
markRestartOutcome(pendingFile, 'migration-failed', migration.reason, log, hasCorrelatedRequest ? requestId : undefined);
|
|
443
|
+
cleanupPendingFile(pendingFile, log, hasCorrelatedRequest ? requestId : undefined);
|
|
391
444
|
process.exit(1);
|
|
392
445
|
}
|
|
393
446
|
// ECWeb is owned by the daemon. Starting it here used to race with the
|
|
@@ -404,22 +457,23 @@ export async function cmdRestartMonitor() {
|
|
|
404
457
|
}, log);
|
|
405
458
|
}
|
|
406
459
|
if (upgradeRolledBack) {
|
|
407
|
-
markRestartOutcome(pendingFile, 'rolled-back', upgradeRolledBack, log);
|
|
460
|
+
markRestartOutcome(pendingFile, 'rolled-back', upgradeRolledBack, log, hasCorrelatedRequest ? requestId : undefined);
|
|
408
461
|
log(`✓ Service restarted after upgrade rollback`);
|
|
409
462
|
}
|
|
410
463
|
else if (buildOutcome.status === 'rolled-back') {
|
|
411
464
|
// 让发起方的 agent 知道改动没生效,而不是以为重启成功了。
|
|
412
|
-
markRestartOutcome(pendingFile, 'rolled-back', buildOutcome.reason, log);
|
|
465
|
+
markRestartOutcome(pendingFile, 'rolled-back', buildOutcome.reason, log, hasCorrelatedRequest ? requestId : undefined);
|
|
413
466
|
log('✓ Service restarted on the previous build');
|
|
414
467
|
}
|
|
415
468
|
else {
|
|
416
469
|
dropStaleBackup(log);
|
|
417
470
|
dropUpgradeBackup(upgradeBackup, log);
|
|
418
471
|
if (upgradeVersions?.to) {
|
|
419
|
-
markRestartOutcome(pendingFile, 'upgraded', `已升级 ${upgradeVersions.from} → ${upgradeVersions.to}`, log);
|
|
472
|
+
markRestartOutcome(pendingFile, 'upgraded', `已升级 ${upgradeVersions.from} → ${upgradeVersions.to}`, log, hasCorrelatedRequest ? requestId : undefined);
|
|
420
473
|
}
|
|
421
474
|
log('✓ Service restarted successfully');
|
|
422
475
|
}
|
|
476
|
+
markRequestReady();
|
|
423
477
|
archiveSelfHealLog(p, log);
|
|
424
478
|
// 发起会话内的“重启成功”通知由新进程自行发送,此处只负责失败/自愈告警。
|
|
425
479
|
await finishSuccessfulRestart();
|
|
@@ -438,7 +492,8 @@ export async function cmdRestartMonitor() {
|
|
|
438
492
|
...upgradeVersions, at: new Date().toISOString(), status: 'rolled-back', error: reason,
|
|
439
493
|
}, log);
|
|
440
494
|
log(`✓ ${reason}`);
|
|
441
|
-
markRestartOutcome(pendingFile, 'rolled-back', reason, log);
|
|
495
|
+
markRestartOutcome(pendingFile, 'rolled-back', reason, log, hasCorrelatedRequest ? requestId : undefined);
|
|
496
|
+
markRequestReady();
|
|
442
497
|
await notifyDaemonOwners(p, `⚠️ ${reason}。当前运行旧版本,请检查后重试。`, log);
|
|
443
498
|
archiveSelfHealLog(p, log);
|
|
444
499
|
process.exit(0);
|
|
@@ -455,7 +510,8 @@ export async function cmdRestartMonitor() {
|
|
|
455
510
|
if (started) {
|
|
456
511
|
const reason = '新编译产物启动失败,已回滚到上一个可用版本';
|
|
457
512
|
log(`✓ ${reason}`);
|
|
458
|
-
markRestartOutcome(pendingFile, 'rolled-back', reason, log);
|
|
513
|
+
markRestartOutcome(pendingFile, 'rolled-back', reason, log, hasCorrelatedRequest ? requestId : undefined);
|
|
514
|
+
markRequestReady();
|
|
459
515
|
await notifyDaemonOwners(p, `⚠️ ${reason}。改动未生效,请检查代码后重试。`, log);
|
|
460
516
|
archiveSelfHealLog(p, log);
|
|
461
517
|
process.exit(0);
|
|
@@ -467,21 +523,21 @@ export async function cmdRestartMonitor() {
|
|
|
467
523
|
if (p.root.startsWith('/tmp/') || process.env.EVOLCORE_TEST === '1') {
|
|
468
524
|
log('❌ Service failed to start (test environment detected, skipping self-heal)');
|
|
469
525
|
await notifyDaemonOwners(p, '❌ 服务启动失败(测试环境,已跳过自动修复)', log);
|
|
470
|
-
cleanupPendingFile(pendingFile, log);
|
|
526
|
+
cleanupPendingFile(pendingFile, log, hasCorrelatedRequest ? requestId : undefined);
|
|
471
527
|
process.exit(1);
|
|
472
528
|
}
|
|
473
529
|
if (hasStartupResourceConflict(p)) {
|
|
474
530
|
const message = '服务启动失败:IPC 管道或端口仍被占用,已跳过自动修复,避免重复连接 AUN。';
|
|
475
531
|
log(message);
|
|
476
532
|
await notifyDaemonOwners(p, `❌ ${message}`, log);
|
|
477
|
-
cleanupPendingFile(pendingFile, log);
|
|
533
|
+
cleanupPendingFile(pendingFile, log, hasCorrelatedRequest ? requestId : undefined);
|
|
478
534
|
process.exit(1);
|
|
479
535
|
}
|
|
480
536
|
if (!isRestartRecoveryEnabled()) {
|
|
481
537
|
const message = '服务启动失败,restartRecovery.enabled 已关闭,已跳过 Claude 自动修复。';
|
|
482
538
|
log(`⚠️ ${message}`);
|
|
483
539
|
await notifyDaemonOwners(p, `⚠️ ${message}`, log);
|
|
484
|
-
cleanupPendingFile(pendingFile, log);
|
|
540
|
+
cleanupPendingFile(pendingFile, log, hasCorrelatedRequest ? requestId : undefined);
|
|
485
541
|
process.exit(1);
|
|
486
542
|
}
|
|
487
543
|
// 启动失败,进入 self-heal 循环
|
|
@@ -493,17 +549,19 @@ export async function cmdRestartMonitor() {
|
|
|
493
549
|
const message = '找不到 claude CLI,已停止自动修复;请检查后台进程 PATH。';
|
|
494
550
|
log(message);
|
|
495
551
|
await notifyDaemonOwners(p, `❌ ${message}`, log);
|
|
496
|
-
cleanupPendingFile(pendingFile, log);
|
|
552
|
+
cleanupPendingFile(pendingFile, log, hasCorrelatedRequest ? requestId : undefined);
|
|
497
553
|
process.exit(1);
|
|
498
554
|
}
|
|
499
555
|
for (let attempt = 1; attempt <= MAX_HEAL_ATTEMPTS; attempt++) {
|
|
500
556
|
// 前置检查:服务可能已被上一轮 claude 修复并启动
|
|
501
557
|
if (isServiceAlive()) {
|
|
502
558
|
log(`✓ Service already running before attempt ${attempt}, skipping`);
|
|
559
|
+
markRequestReady();
|
|
503
560
|
await sendHealSummary(p, attempt - 1, log);
|
|
504
561
|
eventBus.publish({ type: 'self-heal:completed', success: true, attempts: attempt - 1 });
|
|
505
562
|
archiveSelfHealLog(p, log);
|
|
506
|
-
|
|
563
|
+
if (!hasCorrelatedRequest)
|
|
564
|
+
cleanupPendingFile(pendingFile, log);
|
|
507
565
|
process.exit(0);
|
|
508
566
|
}
|
|
509
567
|
log(`Self-heal attempt ${attempt}/${MAX_HEAL_ATTEMPTS}`);
|
|
@@ -513,10 +571,12 @@ export async function cmdRestartMonitor() {
|
|
|
513
571
|
// 后置检查:不管 invokeClaude 返回什么,都检查服务实际状态
|
|
514
572
|
if (isServiceAlive()) {
|
|
515
573
|
log(`✓ Service is running after attempt ${attempt}`);
|
|
574
|
+
markRequestReady();
|
|
516
575
|
await sendHealSummary(p, attempt, log);
|
|
517
576
|
eventBus.publish({ type: 'self-heal:completed', success: true, attempts: attempt });
|
|
518
577
|
archiveSelfHealLog(p, log);
|
|
519
|
-
|
|
578
|
+
if (!hasCorrelatedRequest)
|
|
579
|
+
cleanupPendingFile(pendingFile, log);
|
|
520
580
|
process.exit(0);
|
|
521
581
|
}
|
|
522
582
|
if (!healed) {
|
|
@@ -527,10 +587,12 @@ export async function cmdRestartMonitor() {
|
|
|
527
587
|
started = await spawnAndWaitReady(p, log, READY_TIMEOUT);
|
|
528
588
|
if (started) {
|
|
529
589
|
log(`✓ Self-heal succeeded on attempt ${attempt}`);
|
|
590
|
+
markRequestReady();
|
|
530
591
|
await sendHealSummary(p, attempt, log);
|
|
531
592
|
eventBus.publish({ type: 'self-heal:completed', success: true, attempts: attempt });
|
|
532
593
|
archiveSelfHealLog(p, log);
|
|
533
|
-
|
|
594
|
+
if (!hasCorrelatedRequest)
|
|
595
|
+
cleanupPendingFile(pendingFile, log);
|
|
534
596
|
process.exit(0);
|
|
535
597
|
}
|
|
536
598
|
log(`Attempt ${attempt}: still failing after fix`);
|
|
@@ -538,16 +600,18 @@ export async function cmdRestartMonitor() {
|
|
|
538
600
|
// 全部失败 — 最后再检查一次
|
|
539
601
|
if (isServiceAlive()) {
|
|
540
602
|
log('✓ Service recovered during final check');
|
|
603
|
+
markRequestReady();
|
|
541
604
|
await sendHealSummary(p, MAX_HEAL_ATTEMPTS, log);
|
|
542
605
|
eventBus.publish({ type: 'self-heal:completed', success: true, attempts: MAX_HEAL_ATTEMPTS });
|
|
543
606
|
archiveSelfHealLog(p, log);
|
|
544
|
-
|
|
607
|
+
if (!hasCorrelatedRequest)
|
|
608
|
+
cleanupPendingFile(pendingFile, log);
|
|
545
609
|
process.exit(0);
|
|
546
610
|
}
|
|
547
611
|
log(`❌ All ${MAX_HEAL_ATTEMPTS} self-heal attempts failed`);
|
|
548
612
|
eventBus.publish({ type: 'self-heal:completed', success: false, attempts: MAX_HEAL_ATTEMPTS });
|
|
549
613
|
await notifyDaemonOwners(p, `❌ ${MAX_HEAL_ATTEMPTS} 次自动修复均失败,需要人工介入。\n修复记录:${p.selfHealLog}`, log);
|
|
550
|
-
cleanupPendingFile(pendingFile, log);
|
|
614
|
+
cleanupPendingFile(pendingFile, log, hasCorrelatedRequest ? requestId : undefined);
|
|
551
615
|
process.exit(1);
|
|
552
616
|
}
|
|
553
617
|
/**
|
|
@@ -803,11 +867,15 @@ function runBuildCommand(log) {
|
|
|
803
867
|
* code change took effect. Older daemons ignore unknown fields, so writing this
|
|
804
868
|
* stays safe when the rolled-back artifact predates the field.
|
|
805
869
|
*/
|
|
806
|
-
function markRestartOutcome(pendingFile, outcome, reason, log) {
|
|
870
|
+
function markRestartOutcome(pendingFile, outcome, reason, log, requestId) {
|
|
807
871
|
try {
|
|
808
872
|
if (!fs.existsSync(pendingFile))
|
|
809
873
|
return;
|
|
810
874
|
const info = JSON.parse(fs.readFileSync(pendingFile, 'utf-8'));
|
|
875
|
+
if (requestId && info.requestId !== requestId)
|
|
876
|
+
return;
|
|
877
|
+
if (typeof info.upgradeJobId === 'string')
|
|
878
|
+
updateUpgradeJob(info.upgradeJobId, { lifecycle: { status: outcome, reason, at: new Date().toISOString() } });
|
|
811
879
|
info.outcome = outcome;
|
|
812
880
|
if (reason)
|
|
813
881
|
info.outcomeReason = reason;
|
|
@@ -920,9 +988,25 @@ function dropUpgradeBackup(backupDir, log) {
|
|
|
920
988
|
log('WARN: could not remove upgrade backup');
|
|
921
989
|
}
|
|
922
990
|
}
|
|
923
|
-
function cleanupPendingFile(filePath, log) {
|
|
991
|
+
function cleanupPendingFile(filePath, log, requestId) {
|
|
924
992
|
try {
|
|
925
993
|
if (fs.existsSync(filePath)) {
|
|
994
|
+
if (requestId) {
|
|
995
|
+
const request = readRestartRequest(filePath, requestId);
|
|
996
|
+
if (!request)
|
|
997
|
+
return;
|
|
998
|
+
if (request.status === 'ready') {
|
|
999
|
+
log('Preserved restart-pending.json until the new daemon sends its completion notice');
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
removeRestartRequest(filePath, requestId);
|
|
1003
|
+
try {
|
|
1004
|
+
fs.rmSync(`${filePath}_`, { force: true });
|
|
1005
|
+
}
|
|
1006
|
+
catch { }
|
|
1007
|
+
log('Cleaned up correlated restart-pending.json');
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
926
1010
|
fs.unlinkSync(filePath);
|
|
927
1011
|
log('Cleaned up restart-pending.json');
|
|
928
1012
|
}
|
|
@@ -961,6 +1045,7 @@ async function spawnAndWaitReady(p, log, timeout) {
|
|
|
961
1045
|
...process.env,
|
|
962
1046
|
EVOLCORE_HOME: p.root,
|
|
963
1047
|
EVOLCORE_LAUNCHED_BY: 'restart-monitor',
|
|
1048
|
+
[RESTART_REQUEST_ID_ENV]: '',
|
|
964
1049
|
LOG_LEVEL: process.env.LOG_LEVEL || 'INFO',
|
|
965
1050
|
MESSAGE_LOG: process.env.MESSAGE_LOG || 'true',
|
|
966
1051
|
EVENT_LOG: process.env.EVENT_LOG || 'true',
|
|
@@ -1099,50 +1184,57 @@ function archiveSelfHealLog(p, log) {
|
|
|
1099
1184
|
log(`Archived self-heal log to ${archivePath}`);
|
|
1100
1185
|
}
|
|
1101
1186
|
async function notifyDaemonOwners(p, message, log) {
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
if (!isValidAid(daemonAid)) {
|
|
1109
|
-
log(`Daemon owner notification skipped: invalid daemon.json.aid ${daemonAid}`);
|
|
1110
|
-
return;
|
|
1111
|
-
}
|
|
1112
|
-
const owners = Array.from(new Set((Array.isArray(cfg.owners) ? cfg.owners : [])
|
|
1113
|
-
.map(owner => typeof owner === 'string' ? owner.trim() : '')
|
|
1114
|
-
.filter(Boolean)));
|
|
1115
|
-
if (owners.length === 0) {
|
|
1116
|
-
log('Daemon owner notification skipped: daemon.json.owners not configured');
|
|
1117
|
-
return;
|
|
1118
|
-
}
|
|
1119
|
-
const validOwners = owners.filter(owner => {
|
|
1120
|
-
const valid = isValidAid(owner);
|
|
1121
|
-
if (!valid)
|
|
1122
|
-
log(`Daemon owner notification skipped invalid owner AID: ${owner}`);
|
|
1123
|
-
return valid;
|
|
1124
|
-
});
|
|
1125
|
-
if (validOwners.length === 0)
|
|
1126
|
-
return;
|
|
1127
|
-
const text = formatDaemonOwnerNotice(p, message);
|
|
1128
|
-
for (const owner of validOwners) {
|
|
1129
|
-
try {
|
|
1130
|
-
const result = await withTimeout(msgSend({
|
|
1131
|
-
from: daemonAid,
|
|
1132
|
-
to: owner,
|
|
1133
|
-
body: { mode: 'text', text },
|
|
1134
|
-
slotId: 'restart-monitor',
|
|
1135
|
-
}), OWNER_NOTIFICATION_TIMEOUT_MS);
|
|
1136
|
-
if (!result.ok) {
|
|
1137
|
-
log(`Daemon owner notification failed for ${owner}: ${result.error}`);
|
|
1138
|
-
continue;
|
|
1139
|
-
}
|
|
1140
|
-
log(`Daemon owner notification sent to ${owner}: ${result.message_id}`);
|
|
1187
|
+
try {
|
|
1188
|
+
const cfg = loadDaemonConfig();
|
|
1189
|
+
const daemonAid = typeof cfg.aid === 'string' ? cfg.aid.trim() : '';
|
|
1190
|
+
if (!daemonAid) {
|
|
1191
|
+
log('Daemon owner notification skipped: daemon.json.aid not configured');
|
|
1192
|
+
return;
|
|
1141
1193
|
}
|
|
1142
|
-
|
|
1143
|
-
log(`Daemon owner notification
|
|
1194
|
+
if (!isValidAid(daemonAid)) {
|
|
1195
|
+
log(`Daemon owner notification skipped: invalid daemon.json.aid ${daemonAid}`);
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
const response = await ipcQuery(p.socket, { type: 'aid.ownership', argv: ['aid', 'bind', 'status'] }, OWNER_NOTIFICATION_TIMEOUT_MS).catch(() => null);
|
|
1199
|
+
const state = response?.ok ? response.state : undefined;
|
|
1200
|
+
const owners = state?.subjectAid === daemonAid && state?.consensusStatus === 'consensus'
|
|
1201
|
+
&& typeof state.ownerAid === 'string' && isValidAid(state.ownerAid) ? [state.ownerAid] : [];
|
|
1202
|
+
if (owners.length === 0) {
|
|
1203
|
+
log('Daemon owner notification skipped: fresh protocol consensus unavailable');
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
const validOwners = owners.filter(owner => {
|
|
1207
|
+
const valid = isValidAid(owner);
|
|
1208
|
+
if (!valid)
|
|
1209
|
+
log(`Daemon owner notification skipped invalid owner AID: ${owner}`);
|
|
1210
|
+
return valid;
|
|
1211
|
+
});
|
|
1212
|
+
if (validOwners.length === 0)
|
|
1213
|
+
return;
|
|
1214
|
+
const text = formatDaemonOwnerNotice(p, message);
|
|
1215
|
+
for (const owner of validOwners) {
|
|
1216
|
+
try {
|
|
1217
|
+
const result = await withTimeout(msgSend({
|
|
1218
|
+
from: daemonAid,
|
|
1219
|
+
to: owner,
|
|
1220
|
+
body: { mode: 'text', text },
|
|
1221
|
+
slotId: 'restart-monitor',
|
|
1222
|
+
}), OWNER_NOTIFICATION_TIMEOUT_MS);
|
|
1223
|
+
if (!result.ok) {
|
|
1224
|
+
log(`Daemon owner notification failed for ${owner}: ${result.error}`);
|
|
1225
|
+
continue;
|
|
1226
|
+
}
|
|
1227
|
+
log(`Daemon owner notification sent to ${owner}: ${result.message_id}`);
|
|
1228
|
+
}
|
|
1229
|
+
catch (error) {
|
|
1230
|
+
log(`Daemon owner notification failed for ${owner}: ${error.message?.slice(0, 200) || error}`);
|
|
1231
|
+
}
|
|
1144
1232
|
}
|
|
1145
1233
|
}
|
|
1234
|
+
catch (error) {
|
|
1235
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1236
|
+
log(`WARN: Daemon owner notification failed: ${reason.slice(0, 200)}`);
|
|
1237
|
+
}
|
|
1146
1238
|
}
|
|
1147
1239
|
function formatDaemonOwnerNotice(p, message) {
|
|
1148
1240
|
return [
|
|
@@ -149,7 +149,7 @@ async function cmdFind(args) {
|
|
|
149
149
|
console.log(`ec 会话 id: ${f.sessionId}`);
|
|
150
150
|
console.log(` agentSessionId: ${f.agentSessionId ?? '(空)'}`);
|
|
151
151
|
console.log(` baseagent: ${f.baseagent ?? '(未知)'}`);
|
|
152
|
-
console.log(` threadId: ${f.threadId
|
|
152
|
+
console.log(` threadId: ${f.threadId || '(主会话)'}`);
|
|
153
153
|
if (f.name)
|
|
154
154
|
console.log(` 名称: ${f.name}`);
|
|
155
155
|
if (f.chatType)
|
package/dist/cli/stats.js
CHANGED
|
@@ -41,8 +41,8 @@ Usage: ec stats [options]
|
|
|
41
41
|
--groups [--limit N] 群聊列表(同上)
|
|
42
42
|
--summary 指定时间范围总消耗汇总(token/USD/CNY)
|
|
43
43
|
--peer-detail <id> 指定对端 AID 或 peer_key,按天返回消耗明细
|
|
44
|
-
--task-calls <taskId> 一个 task
|
|
45
|
-
--session-calls <id>
|
|
44
|
+
--task-calls <taskId> 一个 task 的模型用量明细(Codex 为回合/子 agent 汇总)
|
|
45
|
+
--session-calls <id> 一个会话的模型用量明细(Codex 为回合/子 agent 汇总)
|
|
46
46
|
|
|
47
47
|
输出格式:
|
|
48
48
|
--format json JSON 输出
|
|
@@ -417,7 +417,7 @@ export async function handleStats(args) {
|
|
|
417
417
|
console.log();
|
|
418
418
|
return;
|
|
419
419
|
}
|
|
420
|
-
//
|
|
420
|
+
// 模型用量明细
|
|
421
421
|
if ((flags['task-calls'] && typeof flags['task-calls'] === 'string') ||
|
|
422
422
|
(flags['session-calls'] && typeof flags['session-calls'] === 'string')) {
|
|
423
423
|
const byTask = typeof flags['task-calls'] === 'string';
|
|
@@ -431,13 +431,15 @@ export async function handleStats(args) {
|
|
|
431
431
|
console.log('No model calls found.');
|
|
432
432
|
return;
|
|
433
433
|
}
|
|
434
|
-
console.log(`\n${BOLD}📊
|
|
435
|
-
const headers = ['#', 'Model', 'Input', 'Output', 'CacheR', 'CacheW', 'Task', 'Deg'];
|
|
434
|
+
console.log(`\n${BOLD}📊 模型用量明细 — ${key}${RESET}\n`);
|
|
435
|
+
const headers = ['#', 'Model', 'Source', 'Input', 'Output', 'CacheR', 'CacheW', 'Provider Thread', 'Task', 'Deg'];
|
|
436
436
|
const tableRows = rows.map(r => [
|
|
437
437
|
String(r.call_index),
|
|
438
438
|
r.model.split('-').slice(0, 3).join('-'),
|
|
439
|
+
r.source,
|
|
439
440
|
fmtTokens(r.input_tokens), fmtTokens(r.output_tokens),
|
|
440
441
|
fmtTokens(r.cache_read_tokens), fmtTokens(r.cache_creation_tokens),
|
|
442
|
+
r.provider_thread_id ?? '',
|
|
441
443
|
r.task_id, r.degraded ? 'Y' : '',
|
|
442
444
|
]);
|
|
443
445
|
printTable(headers, tableRows);
|