evolcore 0.0.1 → 0.0.2
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 +813 -0
- package/LICENSE +21 -0
- package/MIGRATION-0.5.0.md +378 -0
- package/README.md +318 -14
- package/ROLE_ACCESS_CONTROL.md +174 -0
- package/assets/.env.template +4 -0
- package/assets/brand/evolcore/README.md +19 -0
- package/assets/brand/evolcore/evolcore-app-icon.png +0 -0
- package/assets/brand/evolcore/evolcore-app-icon.svg +13 -0
- package/assets/brand/evolcore/evolcore-brand-board.png +0 -0
- package/assets/brand/evolcore/evolcore-brand-board.svg +126 -0
- package/assets/brand/evolcore/evolcore-logo-kit.zip +0 -0
- package/assets/brand/evolcore/evolcore-logo-reverse.png +0 -0
- package/assets/brand/evolcore/evolcore-logo-reverse.svg +14 -0
- package/assets/brand/evolcore/evolcore-logo.png +0 -0
- package/assets/brand/evolcore/evolcore-logo.svg +14 -0
- package/assets/brand/evolcore/evolcore-mark.png +0 -0
- package/assets/brand/evolcore/evolcore-mark.svg +10 -0
- package/bin/ec-safe-output.js +161 -0
- package/bin/ec.js +29 -0
- package/dist/agents/baseagent.js +163 -0
- package/dist/agents/claude-runner.js +2385 -0
- package/dist/agents/codex-app-server-client.js +448 -0
- package/dist/agents/codex-runner.js +2639 -0
- package/dist/agents/gemini-runner.js +666 -0
- package/dist/agents/runner-types.js +75 -0
- package/dist/aun/aid/agentmd.js +216 -0
- package/dist/aun/aid/client.js +132 -0
- package/dist/aun/aid/control-aid.js +91 -0
- package/dist/aun/aid/identity.js +518 -0
- package/dist/aun/aid/index.js +4 -0
- package/dist/aun/aid/store.js +74 -0
- package/dist/aun/aid/types.js +1 -0
- package/dist/aun/aid/validation.js +21 -0
- package/dist/aun/group-identity.js +10 -0
- package/dist/aun/msg/group-index.js +6 -0
- package/dist/aun/msg/group.js +1231 -0
- package/dist/aun/msg/history.js +123 -0
- package/dist/aun/msg/index.js +5 -0
- package/dist/aun/msg/p2p.js +393 -0
- package/dist/aun/msg/payload-type.js +27 -0
- package/dist/aun/msg/upload.js +137 -0
- package/dist/aun/outbox.js +160 -0
- package/dist/aun/rpc/caller.js +42 -0
- package/dist/aun/rpc/connection.js +25 -0
- package/dist/aun/rpc/index.js +2 -0
- package/dist/aun/service-proxy.js +225 -0
- package/dist/aun/storage/download.js +29 -0
- package/dist/aun/storage/index.js +3 -0
- package/dist/aun/storage/manage.js +10 -0
- package/dist/aun/storage/upload.js +68 -0
- package/dist/channels/aun.js +4147 -0
- package/dist/channels/daemon.js +422 -0
- package/dist/channels/dingtalk.js +649 -0
- package/dist/channels/feishu.js +1789 -0
- package/dist/channels/qqbot.js +409 -0
- package/dist/channels/wechat.js +817 -0
- package/dist/channels/wecom.js +565 -0
- package/dist/cli/agent-command.js +641 -0
- package/dist/cli/agent.js +1059 -0
- package/dist/cli/aun-commands.js +1948 -0
- package/dist/cli/bench.js +1228 -0
- package/dist/cli/cli-argv.js +66 -0
- package/dist/cli/code-stats.js +329 -0
- package/dist/cli/command-log.js +82 -0
- package/dist/cli/config-selector.js +69 -0
- package/dist/cli/config.js +261 -0
- package/dist/cli/ctl-command.js +62 -0
- package/dist/cli/daemon-commands.js +2887 -0
- package/dist/cli/fs-command.js +1447 -0
- package/dist/cli/handoff-command.js +302 -0
- package/dist/cli/help.js +31 -0
- package/dist/cli/index.js +358 -0
- package/dist/cli/init-channel.js +1377 -0
- package/dist/cli/init.js +553 -0
- package/dist/cli/link-rules.js +240 -0
- package/dist/cli/model.js +590 -0
- package/dist/cli/net-check.js +723 -0
- package/dist/cli/queue-command.js +126 -0
- package/dist/cli/response.js +345 -0
- package/dist/cli/restart-monitor.js +456 -0
- package/dist/cli/stats.js +607 -0
- package/dist/cli/task-context.js +80 -0
- package/dist/cli/trigger-command.js +505 -0
- package/dist/cli/version.js +88 -0
- package/dist/cli/watch-logs.js +33 -0
- package/dist/cli/watch-msg.js +673 -0
- package/dist/config/boot-log.js +266 -0
- package/dist/config/builtin-role-templates.js +30 -0
- package/dist/config/builtin-roles.js +85 -0
- package/dist/config/config-batch-get.js +11 -0
- package/dist/config/config-field-policy.js +261 -0
- package/dist/config/config-manager.js +916 -0
- package/dist/config/config-operation-service.js +384 -0
- package/dist/config/contact-book.js +371 -0
- package/dist/config/gateway-config.js +858 -0
- package/dist/config/lifecycle.js +17 -0
- package/dist/config/mention-mode.js +27 -0
- package/dist/config/merge.js +161 -0
- package/dist/config/owner-policy.js +4 -0
- package/dist/config/peer-role-resolver.js +139 -0
- package/dist/config/resolved-config-op.js +483 -0
- package/dist/config/role-config-v4-startup.js +32 -0
- package/dist/config/role-config-v5-startup.js +27 -0
- package/dist/config/role-schema.js +105 -0
- package/dist/config/role-service.js +159 -0
- package/dist/config/role-store.js +204 -0
- package/dist/config/roles.js +55 -0
- package/dist/config/schema-registry.js +154 -0
- package/dist/config/snapshot.js +598 -0
- package/dist/config-store.js +503 -0
- package/dist/core/auth/agent-delegation.js +111 -0
- package/dist/core/auth/auth-gateway.js +166 -0
- package/dist/core/auth/authenticated-actor.js +6 -0
- package/dist/core/auth/authorization-audit.js +110 -0
- package/dist/core/auth/operation-authorizer.js +718 -0
- package/dist/core/auth/operation-catalog.js +675 -0
- package/dist/core/baseagent-loader.js +54 -0
- package/dist/core/bootstrap-service.js +175 -0
- package/dist/core/capability/capability-manager.js +316 -0
- package/dist/core/capability/providers/claude-capability-provider.js +176 -0
- package/dist/core/capability/providers/codex-capability-provider.js +148 -0
- package/dist/core/capability/providers/gemini-capability-provider.js +10 -0
- package/dist/core/capability/types.js +27 -0
- package/dist/core/causation/audit.js +103 -0
- package/dist/core/causation/aun-association.js +111 -0
- package/dist/core/causation/context.js +93 -0
- package/dist/core/causation/index.js +4 -0
- package/dist/core/causation/types.js +2 -0
- package/dist/core/channel-loader.js +277 -0
- package/dist/core/command/agent-control.js +616 -0
- package/dist/core/command/cli-intent-parser.js +225 -0
- package/dist/core/command/command-handler.js +1638 -0
- package/dist/core/command/menu-handler.js +3354 -0
- package/dist/core/command/menu-protocol.js +247 -0
- package/dist/core/command/role-menu.js +1524 -0
- package/dist/core/command/slash-gate.js +148 -0
- package/dist/core/command/slash-handler.js +3056 -0
- package/dist/core/daemon-file-cache.js +216 -0
- package/dist/core/event-bus.js +32 -0
- package/dist/core/event-catalog.js +740 -0
- package/dist/core/evolagent-registry.js +546 -0
- package/dist/core/evolagent.js +331 -0
- package/dist/core/handoff/dispatcher.js +229 -0
- package/dist/core/handoff/mutex.js +46 -0
- package/dist/core/handoff/runtime.js +324 -0
- package/dist/core/handoff/store.js +537 -0
- package/dist/core/handoff/types.js +12 -0
- package/dist/core/inference/text-inference.js +173 -0
- package/dist/core/interaction-registration.js +10 -0
- package/dist/core/interaction-router.js +278 -0
- package/dist/core/message/create-status.js +67 -0
- package/dist/core/message/im-renderer.js +657 -0
- package/dist/core/message/items-formatter.js +76 -0
- package/dist/core/message/logical-queue-bridge.js +123 -0
- package/dist/core/message/message-bridge.js +803 -0
- package/dist/core/message/message-cache.js +56 -0
- package/dist/core/message/message-log.js +339 -0
- package/dist/core/message/message-processor.js +4 -0
- package/dist/core/message/message-queue.js +1436 -0
- package/dist/core/message/message-utils.js +70 -0
- package/dist/core/message/peer-mode.js +105 -0
- package/dist/core/message/pending-hints.js +232 -0
- package/dist/core/message/response-depth.js +33 -0
- package/dist/core/message/response-engine.js +3776 -0
- package/dist/core/message/response-snapshot.js +83 -0
- package/dist/core/message/stream-debouncer.js +130 -0
- package/dist/core/message/stream-idle-monitor.js +124 -0
- package/dist/core/model/config-scope.js +162 -0
- package/dist/core/model/field-scope.js +78 -0
- package/dist/core/model/model-catalog.js +227 -0
- package/dist/core/model/model-diagnostics.js +182 -0
- package/dist/core/model/model-permission.js +90 -0
- package/dist/core/permission/approval-gateway.js +1017 -0
- package/dist/core/permission/ec-command-parser.js +347 -0
- package/dist/core/permission/execution-sandbox.js +16 -0
- package/dist/core/permission/index.js +6 -0
- package/dist/core/permission/mode.js +24 -0
- package/dist/core/permission/sandbox-runtime.js +265 -0
- package/dist/core/permission/tool-policy.js +987 -0
- package/dist/core/permission/unix-socket-policy.js +99 -0
- package/dist/core/protected-paths.js +330 -0
- package/dist/core/relation/peer-identity.js +222 -0
- package/dist/core/relation/peer-key.js +1 -0
- package/dist/core/role/runtime-policy.js +141 -0
- package/dist/core/session/adapters/claude-session-file-adapter.js +218 -0
- package/dist/core/session/adapters/codex-session-file-adapter.js +333 -0
- package/dist/core/session/adapters/gemini-session-file-adapter.js +181 -0
- package/dist/core/session/session-file-adapter.js +7 -0
- package/dist/core/session/session-file-health.js +45 -0
- package/dist/core/session/session-fs-store.js +232 -0
- package/dist/core/session/session-key.js +24 -0
- package/dist/core/session/session-manager.js +1587 -0
- package/dist/core/session/session-mapper.js +100 -0
- package/dist/core/session/session-renew.js +314 -0
- package/dist/core/session/session-title.js +128 -0
- package/dist/core/session/session-turn-coordinator.js +205 -0
- package/dist/core/session/session-turns.js +67 -0
- package/dist/core/system-channels.js +29 -0
- package/dist/eck/baseagent-caps.js +18 -0
- package/dist/eck/detect.js +47 -0
- package/dist/eck/group-rules-sync.js +345 -0
- package/dist/eck/init.js +77 -0
- package/dist/eck/kit-renderer.js +359 -0
- package/dist/eck/manifest-engine.js +446 -0
- package/dist/eck/message-renderer.js +199 -0
- package/dist/eck/rules-loader.js +28 -0
- package/dist/index.js +2870 -4
- package/dist/ipc.js +748 -0
- package/dist/paths.js +262 -0
- package/dist/product.js +18 -0
- package/dist/response-system/context-builder.js +71 -0
- package/dist/response-system/coordinator.js +117 -0
- package/dist/response-system/decision-executor.js +86 -0
- package/dist/response-system/engines/v1/index.js +21 -0
- package/dist/response-system/engines/v1/interactive-flow.js +27 -0
- package/dist/response-system/engines/v1/proactive-flow.js +137 -0
- package/dist/response-system/engines/v1/types.js +1 -0
- package/dist/response-system/extensions.js +41 -0
- package/dist/response-system/index.js +6 -0
- package/dist/response-system/modes/index.js +7 -0
- package/dist/response-system/modes/single-session/index.js +72 -0
- package/dist/response-system/queues/fifo-queue.js +44 -0
- package/dist/response-system/queues/index.js +6 -0
- package/dist/response-system/queues/lifo-queue.js +42 -0
- package/dist/response-system/queues/priority-queue.js +63 -0
- package/dist/response-system/registry.js +97 -0
- package/dist/response-system/resolver.js +37 -0
- package/dist/response-system/selector.js +23 -0
- package/dist/response-system/types.js +7 -0
- package/dist/stats/billing.js +151 -0
- package/dist/stats/budget.js +93 -0
- package/dist/stats/db.js +403 -0
- package/dist/stats/eck-vars.js +89 -0
- package/dist/stats/index.js +11 -0
- package/dist/stats/normalizer.js +80 -0
- package/dist/stats/price-resolver.js +138 -0
- package/dist/stats/query.js +763 -0
- package/dist/stats/role-budget.js +168 -0
- package/dist/stats/writer.js +151 -0
- package/dist/trigger/anomaly-store.js +258 -0
- package/dist/trigger/audit.js +152 -0
- package/dist/trigger/event-source.js +119 -0
- package/dist/trigger/feedback.js +685 -0
- package/dist/trigger/history.js +290 -0
- package/dist/trigger/manager.js +291 -0
- package/dist/trigger/parser.js +520 -0
- package/dist/trigger/patch.js +148 -0
- package/dist/trigger/scheduler.js +1600 -0
- package/dist/trigger/script-executor.js +155 -0
- package/dist/trigger/state.js +145 -0
- package/dist/trigger/types.js +1 -0
- package/dist/trigger/validation.js +621 -0
- package/dist/types.js +12 -0
- package/dist/utils/aid-bind.js +299 -0
- package/dist/utils/atomic-write.js +95 -0
- package/dist/utils/avatar-upload.js +123 -0
- package/dist/utils/cross-platform.js +297 -0
- package/dist/utils/ecweb-utils.js +73 -0
- package/dist/utils/error-dict.json +153 -0
- package/dist/utils/error-utils.js +349 -0
- package/dist/utils/instance-registry.js +437 -0
- package/dist/utils/locale.js +21 -0
- package/dist/utils/log-writer.js +224 -0
- package/dist/utils/logger.js +89 -0
- package/dist/utils/markdown-to-plain-text.js +20 -0
- package/dist/utils/media-cache.js +271 -0
- package/dist/utils/model-prices.jsonl +17 -0
- package/dist/utils/npm-ops.js +210 -0
- package/dist/utils/process-introspect.js +133 -0
- package/dist/utils/process-tree-stats.js +271 -0
- package/dist/utils/project-path.js +74 -0
- package/dist/utils/stats.js +410 -0
- package/dist/utils/tool-summary.js +284 -0
- package/dist/utils/welcome.js +268 -0
- package/kits/docs/GUIDE.md +20 -0
- package/kits/docs/INDEX.md +65 -0
- package/kits/docs/aun/CHEATSHEET.md +19 -0
- package/kits/docs/aun/SYNC_PROTOCOL.md +15 -0
- package/kits/docs/channels/aun.md +65 -0
- package/kits/docs/channels/feishu.md +56 -0
- package/kits/docs/context-assembly.md +366 -0
- package/kits/docs/eck_templates/GUIDE.template.md +22 -0
- package/kits/docs/eck_templates/INDEX.template.md +28 -0
- package/kits/docs/eck_templates/path-registry.template.md +33 -0
- package/kits/docs/eck_templates/runtime.template.md +19 -0
- package/kits/docs/evolcore/INDEX.md +66 -0
- package/kits/docs/evolcore/agent.md +69 -0
- package/kits/docs/evolcore/aid.md +49 -0
- package/kits/docs/evolcore/config.md +149 -0
- package/kits/docs/evolcore/ctl.md +46 -0
- package/kits/docs/evolcore/event.md +216 -0
- package/kits/docs/evolcore/fs-architecture.md +1215 -0
- package/kits/docs/evolcore/fs.md +101 -0
- package/kits/docs/evolcore/group-fs.md +17 -0
- package/kits/docs/evolcore/group-rules.md +226 -0
- package/kits/docs/evolcore/group.md +141 -0
- package/kits/docs/evolcore/model.md +47 -0
- package/kits/docs/evolcore/msg.md +130 -0
- package/kits/docs/evolcore/response.md +80 -0
- package/kits/docs/evolcore/rpc.md +35 -0
- package/kits/docs/evolcore/self-summary.md +29 -0
- package/kits/docs/evolcore/stats.md +70 -0
- package/kits/docs/evolcore/storage.md +49 -0
- package/kits/docs/evolcore/trigger.md +524 -0
- package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -0
- package/kits/docs/identity/PATH_OPS.md +16 -0
- package/kits/docs/identity/ROLE_DETAIL.md +23 -0
- package/kits/docs/identity/identity-tools.md +26 -0
- package/kits/docs/path-registry.md +43 -0
- package/kits/docs/prompt-loading-architecture.md +266 -0
- package/kits/docs/venues/aun-group.md +45 -0
- package/kits/docs/venues/aun-private.md +10 -0
- package/kits/docs/venues/client-desktop.md +10 -0
- package/kits/docs/venues/client-mobile.md +10 -0
- package/kits/docs/venues/feishu-group.md +13 -0
- package/kits/docs/venues/feishu-private.md +9 -0
- package/kits/docs/venues/group.md +25 -0
- package/kits/docs/venues/private.md +10 -0
- package/kits/eck_manifest.auxiliary.json +43 -0
- package/kits/eck_manifest.json +191 -0
- package/kits/eck_message_manifest.json +63 -0
- package/kits/migrations/README-role-config-v4.md +32 -0
- package/kits/migrations/migrate-role-config-v4.mjs +623 -0
- package/kits/migrations/migrate-role-config-v5.mjs +346 -0
- package/kits/migrations/rename-config-file.mjs +99 -0
- package/kits/rules/01-overview.md +142 -0
- package/kits/rules/02-navigation.md +76 -0
- package/kits/rules/03-identity.md +34 -0
- package/kits/rules/04-relation.md +59 -0
- package/kits/rules/05-venue.md +44 -0
- package/kits/rules/06-channel.md +59 -0
- package/kits/schemas/_meta.json +29 -0
- package/kits/schemas/agent-config.schema.1.json +177 -0
- package/kits/schemas/agent-config.schema.2.json +239 -0
- package/kits/schemas/agent-config.schema.3.json +119 -0
- package/kits/schemas/agent-config.schema.4.json +208 -0
- package/kits/schemas/agent-config.schema.5.json +326 -0
- package/kits/schemas/contact-book.schema.1.json +36 -0
- package/kits/schemas/daemon.schema.1.json +90 -0
- package/kits/schemas/defaults.schema.1.json +81 -0
- package/kits/schemas/menu-exec-schema-commands.md +208 -0
- package/kits/schemas/migrations/README.md +28 -0
- package/kits/schemas/relation-config.schema.1.json +158 -0
- package/kits/schemas/relation-config.schema.2.json +73 -0
- package/kits/schemas/relation-config.schema.3.json +50 -0
- package/kits/schemas/relation-config.schema.4.json +47 -0
- package/kits/schemas/role-config.schema.1.json +200 -0
- package/kits/schemas/role-registry.schema.1.json +35 -0
- package/kits/schemas/single-session.schema.1.json +31 -0
- package/kits/templates/bootstrap-welcome.md +15 -0
- package/kits/templates/message-fragments/handoff-request-to-target.md +13 -0
- package/kits/templates/message-fragments/handoff-response-to-origin.md +10 -0
- package/kits/templates/message-fragments/inject-default.md +2 -0
- package/kits/templates/message-fragments/item.md +2 -0
- package/kits/templates/roles/admin.json +8 -0
- package/kits/templates/roles/member.json +40 -0
- package/kits/templates/roles/owner.json +8 -0
- package/kits/templates/roles/visitor.json +39 -0
- package/kits/templates/system-fragments/baseagent.md +14 -0
- package/kits/templates/system-fragments/bootstrap.md +16 -0
- package/kits/templates/system-fragments/channel.md +48 -0
- package/kits/templates/system-fragments/commands.md +26 -0
- package/kits/templates/system-fragments/identity.md +11 -0
- package/kits/templates/system-fragments/relation.md +19 -0
- package/kits/templates/system-fragments/session.md +53 -0
- package/kits/templates/system-fragments/venue.md +31 -0
- package/package.json +50 -15
- package/dist/index.d.ts +0 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { spawn, execFile } from 'child_process';
|
|
4
|
+
import { promisify } from 'util';
|
|
5
|
+
import { resolvePaths, getPackageRoot } from '../paths.js';
|
|
6
|
+
import { loadDaemonConfig } from '../config-store.js';
|
|
7
|
+
import * as platform from '../utils/cross-platform.js';
|
|
8
|
+
import { EventBus } from '../core/event-bus.js';
|
|
9
|
+
import { tryUpgrade, tryUpgradeAunSdk, tryUpgradeGlobalPkg, resolveGlobalPkg } from '../utils/npm-ops.js';
|
|
10
|
+
import { resolveAunCoreSdkPkg, AUN_CORE_SDK_PKG } from '../aun/aid/client.js';
|
|
11
|
+
import { isValidAid } from '../aun/aid/index.js';
|
|
12
|
+
import { msgSend } from '../aun/msg/index.js';
|
|
13
|
+
import { scanInstances, cleanupInstances, writeRestartMonitor, removeRestartMonitor, isRestartMonitorWinner } from '../utils/instance-registry.js';
|
|
14
|
+
import { WEB_PACKAGE_NAME } from '../product.js';
|
|
15
|
+
const execFileAsync = promisify(execFile);
|
|
16
|
+
// 清理 Claude Code 环境变量,防止 SDK 认为是嵌套会话
|
|
17
|
+
function cleanEnv() {
|
|
18
|
+
for (const key of [
|
|
19
|
+
'CLAUDECODE', 'CLAUDE_CODE_ENTRYPOINT',
|
|
20
|
+
'CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS',
|
|
21
|
+
'CLAUDE_CONFIG_DIR',
|
|
22
|
+
]) {
|
|
23
|
+
delete process.env[key];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export async function cmdRestartMonitor() {
|
|
27
|
+
const p = resolvePaths();
|
|
28
|
+
const restartLog = path.join(p.logs, 'restart.log');
|
|
29
|
+
const MAX_HEAL_ATTEMPTS = 3;
|
|
30
|
+
const READY_TIMEOUT = 30000; // 30s(AUN sidecar + 外部渠道连接预留)
|
|
31
|
+
const HEAL_TIMEOUT = 30 * 60 * 1000; // 30 分钟,让 claude 自然结束
|
|
32
|
+
const eventBus = new EventBus();
|
|
33
|
+
const log = (msg) => {
|
|
34
|
+
const _d = new Date();
|
|
35
|
+
const _p = (n) => String(n).padStart(2, '0');
|
|
36
|
+
const ts = `${_d.getFullYear()}-${_p(_d.getMonth() + 1)}-${_p(_d.getDate())} ${_p(_d.getHours())}:${_p(_d.getMinutes())}:${_p(_d.getSeconds())}`;
|
|
37
|
+
const line = `[${ts}] ${msg}\n`;
|
|
38
|
+
fs.appendFileSync(restartLog, line);
|
|
39
|
+
};
|
|
40
|
+
// 单实例保护:pre-check + post-write 自检(同 main 进程)
|
|
41
|
+
{
|
|
42
|
+
const pre = scanInstances();
|
|
43
|
+
const aliveOthers = pre.restartMonitors.filter(m => m.alive && m.record.pid !== process.pid);
|
|
44
|
+
if (aliveOthers.length > 0) {
|
|
45
|
+
log(`Another restart-monitor already running (PID: ${aliveOthers.map(m => m.record.pid).join(', ')}), exiting`);
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// 立即登记自己;exit 路径上自动清理 record
|
|
50
|
+
writeRestartMonitor();
|
|
51
|
+
process.on('exit', () => removeRestartMonitor());
|
|
52
|
+
// post-write 自检:(startedAt, pid) 选最早赢家
|
|
53
|
+
{
|
|
54
|
+
const verdict = isRestartMonitorWinner();
|
|
55
|
+
if (!verdict.winner) {
|
|
56
|
+
log(`Lost restart-monitor election to PID ${verdict.conflictingPid}, yielding`);
|
|
57
|
+
removeRestartMonitor();
|
|
58
|
+
process.exit(0);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/** 检查服务是否已经在运行(ready signal 存在 + 至少一个活 main) */
|
|
62
|
+
const isServiceAlive = () => {
|
|
63
|
+
if (!fs.existsSync(p.readySignal))
|
|
64
|
+
return false;
|
|
65
|
+
const s = scanInstances();
|
|
66
|
+
return s.mains.some(m => m.alive);
|
|
67
|
+
};
|
|
68
|
+
log('Restart monitor started');
|
|
69
|
+
// restart-pending.json 只留给新主进程发送发起会话内的“重启成功”回执。
|
|
70
|
+
const pendingFile = path.join(p.dataDir, 'restart-pending.json');
|
|
71
|
+
// 等待所有活 main 进程退出(可能不止一个)
|
|
72
|
+
const oldStatus = scanInstances();
|
|
73
|
+
const aliveMains = oldStatus.mains.filter(m => m.alive);
|
|
74
|
+
if (aliveMains.length > 0) {
|
|
75
|
+
const oldPids = aliveMains.map(m => m.record.pid);
|
|
76
|
+
log(`Monitoring ${oldPids.length} main process(es): ${oldPids.join(', ')}`);
|
|
77
|
+
// 先并行 SIGTERM 通知所有活 main
|
|
78
|
+
for (const pid of oldPids) {
|
|
79
|
+
try {
|
|
80
|
+
platform.killProcess(pid, false);
|
|
81
|
+
}
|
|
82
|
+
catch { }
|
|
83
|
+
}
|
|
84
|
+
await Promise.all(oldPids.map(oldPid => new Promise((resolve) => {
|
|
85
|
+
let waited = 0;
|
|
86
|
+
const interval = setInterval(() => {
|
|
87
|
+
waited++;
|
|
88
|
+
if (!platform.isProcessRunning(oldPid)) {
|
|
89
|
+
clearInterval(interval);
|
|
90
|
+
log(`Process ${oldPid} has exited`);
|
|
91
|
+
resolve();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (waited >= 30) {
|
|
95
|
+
clearInterval(interval);
|
|
96
|
+
log(`ERROR: Process ${oldPid} still running after 30s, force killing`);
|
|
97
|
+
platform.killProcess(oldPid, true);
|
|
98
|
+
resolve();
|
|
99
|
+
}
|
|
100
|
+
}, 1000);
|
|
101
|
+
})));
|
|
102
|
+
await sleep(3000);
|
|
103
|
+
cleanupInstances();
|
|
104
|
+
}
|
|
105
|
+
// 版本检查与自动升级
|
|
106
|
+
log('Checking for updates...');
|
|
107
|
+
const upgrade = await tryUpgrade();
|
|
108
|
+
switch (upgrade.status) {
|
|
109
|
+
case 'upgraded':
|
|
110
|
+
log(`✅ Upgraded: ${upgrade.from} → ${upgrade.to}`);
|
|
111
|
+
await notifyDaemonOwners(p, `📦 已升级 ${upgrade.from} → ${upgrade.to}`, log);
|
|
112
|
+
break;
|
|
113
|
+
case 'no-update':
|
|
114
|
+
log(`Already up to date (${upgrade.from})`);
|
|
115
|
+
break;
|
|
116
|
+
case 'skipped':
|
|
117
|
+
log(upgrade.error
|
|
118
|
+
? 'Skipped upgrade (network unavailable)'
|
|
119
|
+
: 'Skipped upgrade check (dev mode)');
|
|
120
|
+
break;
|
|
121
|
+
case 'failed':
|
|
122
|
+
log(`⚠ Upgrade failed (${upgrade.from} → ${upgrade.to}): ${upgrade.error}`);
|
|
123
|
+
await notifyDaemonOwners(p, `⚠️ evolcore 升级失败,使用当前版本继续\n${upgrade.error ?? ''}`.trim(), log);
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
// AUN SDK 版本检查与升级
|
|
127
|
+
const aunUpgrade = await tryUpgradeAunSdk(resolveAunCoreSdkPkg, AUN_CORE_SDK_PKG);
|
|
128
|
+
switch (aunUpgrade.status) {
|
|
129
|
+
case 'upgraded':
|
|
130
|
+
log(`✅ AUN SDK upgraded: ${aunUpgrade.from} → ${aunUpgrade.to}`);
|
|
131
|
+
await notifyDaemonOwners(p, `📦 AUN SDK 已升级 ${aunUpgrade.from} → ${aunUpgrade.to}`, log);
|
|
132
|
+
break;
|
|
133
|
+
case 'no-update':
|
|
134
|
+
break;
|
|
135
|
+
case 'failed':
|
|
136
|
+
log(`⚠ AUN SDK upgrade failed (${aunUpgrade.from} → ${aunUpgrade.to}): ${aunUpgrade.error}`);
|
|
137
|
+
await notifyDaemonOwners(p, `⚠️ AUN SDK 升级失败,使用当前版本继续\n${aunUpgrade.error ?? ''}`.trim(), log);
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
// EC Web 版本检查与升级(已安装才检查)
|
|
141
|
+
const ecwebUpgrade = await tryUpgradeGlobalPkg(() => resolveGlobalPkg(WEB_PACKAGE_NAME), WEB_PACKAGE_NAME);
|
|
142
|
+
switch (ecwebUpgrade.status) {
|
|
143
|
+
case 'upgraded':
|
|
144
|
+
log(`✅ ${WEB_PACKAGE_NAME} upgraded: ${ecwebUpgrade.from} → ${ecwebUpgrade.to}`);
|
|
145
|
+
await notifyDaemonOwners(p, `📦 ${WEB_PACKAGE_NAME} 已升级 ${ecwebUpgrade.from} → ${ecwebUpgrade.to}`, log);
|
|
146
|
+
break;
|
|
147
|
+
case 'no-update':
|
|
148
|
+
break;
|
|
149
|
+
case 'failed':
|
|
150
|
+
log(`⚠ ${WEB_PACKAGE_NAME} upgrade failed (${ecwebUpgrade.from} → ${ecwebUpgrade.to}): ${ecwebUpgrade.error}`);
|
|
151
|
+
await notifyDaemonOwners(p, `⚠️ ${WEB_PACKAGE_NAME} 升级失败,使用当前版本继续\n${ecwebUpgrade.error ?? ''}`.trim(), log);
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
// 启动并检测 ready signal
|
|
155
|
+
let started = await spawnAndWaitReady(p, log, READY_TIMEOUT);
|
|
156
|
+
if (started) {
|
|
157
|
+
log('✓ Service restarted successfully');
|
|
158
|
+
archiveSelfHealLog(p, log);
|
|
159
|
+
// 发起会话内的“重启成功”通知由新进程自行发送,此处只负责失败/自愈告警。
|
|
160
|
+
process.exit(0);
|
|
161
|
+
}
|
|
162
|
+
// 启动失败 — 测试环境下跳过 self-heal(避免 claude -p 污染会话列表、误杀生产进程)
|
|
163
|
+
if (p.root.startsWith('/tmp/') || process.env.EVOLCORE_TEST === '1') {
|
|
164
|
+
log('❌ Service failed to start (test environment detected, skipping self-heal)');
|
|
165
|
+
await notifyDaemonOwners(p, '❌ 服务启动失败(测试环境,已跳过自动修复)', log);
|
|
166
|
+
cleanupPendingFile(pendingFile, log);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
// 启动失败,进入 self-heal 循环
|
|
170
|
+
log('❌ Service failed to start, entering self-heal loop');
|
|
171
|
+
eventBus.publish({ type: 'self-heal:started', reason: 'Service failed to start after restart' });
|
|
172
|
+
await notifyDaemonOwners(p, '⚠️ 服务启动失败,正在尝试自动修复...', log);
|
|
173
|
+
for (let attempt = 1; attempt <= MAX_HEAL_ATTEMPTS; attempt++) {
|
|
174
|
+
// 前置检查:服务可能已被上一轮 claude 修复并启动
|
|
175
|
+
if (isServiceAlive()) {
|
|
176
|
+
log(`✓ Service already running before attempt ${attempt}, skipping`);
|
|
177
|
+
await sendHealSummary(p, attempt - 1, log);
|
|
178
|
+
eventBus.publish({ type: 'self-heal:completed', success: true, attempts: attempt - 1 });
|
|
179
|
+
archiveSelfHealLog(p, log);
|
|
180
|
+
cleanupPendingFile(pendingFile, log);
|
|
181
|
+
process.exit(0);
|
|
182
|
+
}
|
|
183
|
+
log(`Self-heal attempt ${attempt}/${MAX_HEAL_ATTEMPTS}`);
|
|
184
|
+
eventBus.publish({ type: 'self-heal:attempt', attemptNumber: attempt, maxAttempts: MAX_HEAL_ATTEMPTS });
|
|
185
|
+
await notifyDaemonOwners(p, `🔧 自动修复中(第 ${attempt}/${MAX_HEAL_ATTEMPTS} 次)...`, log);
|
|
186
|
+
const healed = await invokeClaude(p, attempt, MAX_HEAL_ATTEMPTS, HEAL_TIMEOUT, log);
|
|
187
|
+
// 后置检查:不管 invokeClaude 返回什么,都检查服务实际状态
|
|
188
|
+
if (isServiceAlive()) {
|
|
189
|
+
log(`✓ Service is running after attempt ${attempt}`);
|
|
190
|
+
await sendHealSummary(p, attempt, log);
|
|
191
|
+
eventBus.publish({ type: 'self-heal:completed', success: true, attempts: attempt });
|
|
192
|
+
archiveSelfHealLog(p, log);
|
|
193
|
+
cleanupPendingFile(pendingFile, log);
|
|
194
|
+
process.exit(0);
|
|
195
|
+
}
|
|
196
|
+
if (!healed) {
|
|
197
|
+
log(`Self-heal attempt ${attempt} failed (claude invocation error)`);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
// claude 正常完成但服务没自动启动,尝试 spawn
|
|
201
|
+
started = await spawnAndWaitReady(p, log, READY_TIMEOUT);
|
|
202
|
+
if (started) {
|
|
203
|
+
log(`✓ Self-heal succeeded on attempt ${attempt}`);
|
|
204
|
+
await sendHealSummary(p, attempt, log);
|
|
205
|
+
eventBus.publish({ type: 'self-heal:completed', success: true, attempts: attempt });
|
|
206
|
+
archiveSelfHealLog(p, log);
|
|
207
|
+
cleanupPendingFile(pendingFile, log);
|
|
208
|
+
process.exit(0);
|
|
209
|
+
}
|
|
210
|
+
log(`Attempt ${attempt}: still failing after fix`);
|
|
211
|
+
}
|
|
212
|
+
// 全部失败 — 最后再检查一次
|
|
213
|
+
if (isServiceAlive()) {
|
|
214
|
+
log('✓ Service recovered during final check');
|
|
215
|
+
await sendHealSummary(p, MAX_HEAL_ATTEMPTS, log);
|
|
216
|
+
eventBus.publish({ type: 'self-heal:completed', success: true, attempts: MAX_HEAL_ATTEMPTS });
|
|
217
|
+
archiveSelfHealLog(p, log);
|
|
218
|
+
cleanupPendingFile(pendingFile, log);
|
|
219
|
+
process.exit(0);
|
|
220
|
+
}
|
|
221
|
+
log(`❌ All ${MAX_HEAL_ATTEMPTS} self-heal attempts failed`);
|
|
222
|
+
eventBus.publish({ type: 'self-heal:completed', success: false, attempts: MAX_HEAL_ATTEMPTS });
|
|
223
|
+
await notifyDaemonOwners(p, `❌ ${MAX_HEAL_ATTEMPTS} 次自动修复均失败,需要人工介入。\n修复记录:${p.selfHealLog}`, log);
|
|
224
|
+
cleanupPendingFile(pendingFile, log);
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* 发送 self-heal 修复成功小结(从 self-heal.md 提取摘要)
|
|
229
|
+
*/
|
|
230
|
+
async function sendHealSummary(p, attempts, log) {
|
|
231
|
+
let summary = `✅ 自动修复成功(第 ${attempts || 1} 次尝试)`;
|
|
232
|
+
try {
|
|
233
|
+
if (fs.existsSync(p.selfHealLog)) {
|
|
234
|
+
const content = fs.readFileSync(p.selfHealLog, 'utf-8');
|
|
235
|
+
// 提取最后一个 ## 章节的要点
|
|
236
|
+
const sections = content.split(/^## /m).filter(Boolean);
|
|
237
|
+
const last = sections[sections.length - 1];
|
|
238
|
+
if (last) {
|
|
239
|
+
const lines = last.split('\n').filter(l => l.startsWith('- ')).map(l => l.trim());
|
|
240
|
+
if (lines.length > 0) {
|
|
241
|
+
summary += '\n' + lines.join('\n');
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
catch { }
|
|
247
|
+
summary += '\n\n⚠️ 修复前进行中的任务已中断,如需继续请重新发送。';
|
|
248
|
+
await notifyDaemonOwners(p, summary, log);
|
|
249
|
+
}
|
|
250
|
+
function sleep(ms) {
|
|
251
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
252
|
+
}
|
|
253
|
+
function cleanupPendingFile(filePath, log) {
|
|
254
|
+
try {
|
|
255
|
+
if (fs.existsSync(filePath)) {
|
|
256
|
+
fs.unlinkSync(filePath);
|
|
257
|
+
log('Cleaned up restart-pending.json');
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
catch { }
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* 启动新进程并等待 ready.signal
|
|
264
|
+
*/
|
|
265
|
+
async function spawnAndWaitReady(p, log, timeout) {
|
|
266
|
+
// 删除旧的 ready signal
|
|
267
|
+
try {
|
|
268
|
+
fs.unlinkSync(p.readySignal);
|
|
269
|
+
}
|
|
270
|
+
catch { }
|
|
271
|
+
// 清理残留 instance 文件和进程
|
|
272
|
+
cleanupInstances();
|
|
273
|
+
cleanEnv();
|
|
274
|
+
const stdoutLog = path.join(p.logs, 'stdout.log');
|
|
275
|
+
const out = fs.openSync(stdoutLog, 'a');
|
|
276
|
+
const err = fs.openSync(stdoutLog, 'a');
|
|
277
|
+
const appMain = path.join(getPackageRoot(), 'dist', 'index.js');
|
|
278
|
+
const child = spawn('node', ['--no-warnings=ExperimentalWarning', appMain], {
|
|
279
|
+
detached: true,
|
|
280
|
+
stdio: ['ignore', out, err],
|
|
281
|
+
windowsHide: true,
|
|
282
|
+
env: {
|
|
283
|
+
...process.env,
|
|
284
|
+
EVOLCORE_HOME: p.root,
|
|
285
|
+
EVOLCORE_LAUNCHED_BY: 'restart-monitor',
|
|
286
|
+
LOG_LEVEL: process.env.LOG_LEVEL || 'INFO',
|
|
287
|
+
MESSAGE_LOG: process.env.MESSAGE_LOG || 'true',
|
|
288
|
+
EVENT_LOG: process.env.EVENT_LOG || 'true',
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
const childPid = child.pid;
|
|
292
|
+
child.unref();
|
|
293
|
+
log(`Spawned new process PID: ${childPid}, waiting for ready signal...`);
|
|
294
|
+
// 轮询等待 ready.signal 出现
|
|
295
|
+
const start = Date.now();
|
|
296
|
+
while (Date.now() - start < timeout) {
|
|
297
|
+
await sleep(500);
|
|
298
|
+
// 进程已退出则提前失败
|
|
299
|
+
if (!platform.isProcessRunning(childPid)) {
|
|
300
|
+
log('Process exited before ready signal');
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
if (fs.existsSync(p.readySignal)) {
|
|
304
|
+
log('Ready signal detected');
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
log(`Ready signal not received within ${timeout / 1000}s`);
|
|
309
|
+
// 超时后杀掉进程
|
|
310
|
+
if (platform.isProcessRunning(childPid)) {
|
|
311
|
+
platform.killProcess(childPid);
|
|
312
|
+
}
|
|
313
|
+
cleanupInstances();
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* 调用 claude CLI 进行自动修复
|
|
318
|
+
*/
|
|
319
|
+
async function invokeClaude(p, attempt, maxAttempts, timeout, log) {
|
|
320
|
+
const projectDir = getPackageRoot();
|
|
321
|
+
const selfHealLog = p.selfHealLog;
|
|
322
|
+
const stdoutLog = path.join(p.logs, 'stdout.log');
|
|
323
|
+
const selfHealExists = fs.existsSync(selfHealLog) ? '存在,请先阅读之前的修复记录' : '不存在(首次修复)';
|
|
324
|
+
const prompt = `EvolCore 服务启动失败,需要你诊断并修复。这是第 ${attempt}/${maxAttempts} 次自动修复尝试。
|
|
325
|
+
|
|
326
|
+
关键信息:
|
|
327
|
+
- 项目目录:${projectDir}
|
|
328
|
+
- EVOLCORE_HOME:${p.root}
|
|
329
|
+
- 错误日志:${stdoutLog}
|
|
330
|
+
- 主日志:${p.logs}/daemon-*.log(按小时切片,读最新的那个,包含 config 校验失败等关键错误)
|
|
331
|
+
- 修复记录:${selfHealLog}(${selfHealExists})
|
|
332
|
+
|
|
333
|
+
⚠️ 重要诊断技巧:
|
|
334
|
+
- stdout.log 可能是空的(进程秒退时 logger 输出不会到 stdout),一定要同时读 daemon-*.log 最新文件
|
|
335
|
+
- 必须实际运行进程来复现错误:\`EVOLCORE_HOME=${p.root} node dist/index.js 2>&1\`,观察输出和退出码
|
|
336
|
+
- 检查是否有旧进程仍在运行:\`ps aux | grep 'node.*dist/index.js' | grep -v grep\`,旧进程可能占用端口或锁文件
|
|
337
|
+
- 可以运行 \`EVOLCORE_HOME=${p.root} node dist/cli/index.js diagnose\` 快速检查配置和数据库
|
|
338
|
+
- 如果进程无任何输出就 exit(1),说明是 process.exit(1) 被显式调用,搜索源码中所有 process.exit(1) 位置
|
|
339
|
+
- 配置文件使用双 rename 原子写(foo.json → foo.json_ → foo.json__),崩溃时可从 foo.json_ 恢复
|
|
340
|
+
|
|
341
|
+
请执行以下步骤:
|
|
342
|
+
1. 读取 ${stdoutLog} 和 ${p.logs}/daemon-*.log(最新文件)的最后 50 行
|
|
343
|
+
2. 运行 \`EVOLCORE_HOME=${p.root} node dist/index.js 2>&1\` 复现错误(设置 10 秒超时)
|
|
344
|
+
3. 如果 ${selfHealLog} 存在,先阅读之前的修复记录,避免重复尝试已失败的方案
|
|
345
|
+
4. 根据实际复现的错误修复代码
|
|
346
|
+
5. 执行 npm run build 确认编译通过
|
|
347
|
+
6. 验证修复:启动服务确认 ready.signal 已写入,然后执行 \`EVOLCORE_HOME=${p.root} node dist/cli/index.js stop\` 优雅停止(restart-monitor 会负责最终启动)
|
|
348
|
+
7. 将本次修复内容追加到 ${selfHealLog},格式:
|
|
349
|
+
## 第 ${attempt} 次修复 - {时间}
|
|
350
|
+
- 错误原因:...
|
|
351
|
+
- 修复方案:...
|
|
352
|
+
- 修改文件:...
|
|
353
|
+
|
|
354
|
+
注意:只修复导致启动失败的问题,不要做额外的重构或优化。`;
|
|
355
|
+
try {
|
|
356
|
+
log(`Invoking claude CLI (attempt ${attempt}, timeout ${timeout / 60000}min)...`);
|
|
357
|
+
const { stdout, stderr } = await execFileAsync('claude', [
|
|
358
|
+
'-p', prompt,
|
|
359
|
+
'--allowedTools', 'Read,Write,Edit,Bash,Glob,Grep',
|
|
360
|
+
'--output-format', 'text',
|
|
361
|
+
'--no-session-persistence',
|
|
362
|
+
], {
|
|
363
|
+
cwd: projectDir,
|
|
364
|
+
timeout,
|
|
365
|
+
env: { ...process.env, CLAUDE_CODE_ENTRYPOINT: 'cli' },
|
|
366
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
367
|
+
});
|
|
368
|
+
if (stdout)
|
|
369
|
+
log(`Claude output: ${stdout.slice(0, 500)}`);
|
|
370
|
+
if (stderr)
|
|
371
|
+
log(`Claude stderr: ${stderr.slice(0, 500)}`);
|
|
372
|
+
log(`Claude CLI completed (attempt ${attempt})`);
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
catch (error) {
|
|
376
|
+
if (error.killed) {
|
|
377
|
+
log(`Claude CLI timeout after ${timeout / 60000}min (attempt ${attempt})`);
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
log(`Claude CLI error: exit code ${error.code ?? 'unknown'} (attempt ${attempt})`);
|
|
381
|
+
}
|
|
382
|
+
if (error.stdout)
|
|
383
|
+
log(`Claude output: ${String(error.stdout).slice(0, 500)}`);
|
|
384
|
+
if (error.stderr) {
|
|
385
|
+
const stderr = String(error.stderr).replace(/Warning: no stdin.*\n?/g, '').trim();
|
|
386
|
+
if (stderr)
|
|
387
|
+
log(`Claude stderr: ${stderr.slice(0, 300)}`);
|
|
388
|
+
}
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* 归档 self-heal.md
|
|
394
|
+
*/
|
|
395
|
+
function archiveSelfHealLog(p, log) {
|
|
396
|
+
if (!fs.existsSync(p.selfHealLog))
|
|
397
|
+
return;
|
|
398
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '').slice(0, 15);
|
|
399
|
+
const archivePath = path.join(p.logs, `self-heal-${timestamp}.md`);
|
|
400
|
+
fs.renameSync(p.selfHealLog, archivePath);
|
|
401
|
+
log(`Archived self-heal log to ${archivePath}`);
|
|
402
|
+
}
|
|
403
|
+
async function notifyDaemonOwners(p, message, log) {
|
|
404
|
+
const cfg = loadDaemonConfig();
|
|
405
|
+
const daemonAid = typeof cfg.aid === 'string' ? cfg.aid.trim() : '';
|
|
406
|
+
if (!daemonAid) {
|
|
407
|
+
log('Daemon owner notification skipped: daemon.json.aid not configured');
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (!isValidAid(daemonAid)) {
|
|
411
|
+
log(`Daemon owner notification skipped: invalid daemon.json.aid ${daemonAid}`);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
const owners = Array.from(new Set((Array.isArray(cfg.owners) ? cfg.owners : [])
|
|
415
|
+
.map(owner => typeof owner === 'string' ? owner.trim() : '')
|
|
416
|
+
.filter(Boolean)));
|
|
417
|
+
if (owners.length === 0) {
|
|
418
|
+
log('Daemon owner notification skipped: daemon.json.owners not configured');
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const validOwners = owners.filter(owner => {
|
|
422
|
+
const valid = isValidAid(owner);
|
|
423
|
+
if (!valid)
|
|
424
|
+
log(`Daemon owner notification skipped invalid owner AID: ${owner}`);
|
|
425
|
+
return valid;
|
|
426
|
+
});
|
|
427
|
+
if (validOwners.length === 0)
|
|
428
|
+
return;
|
|
429
|
+
const text = formatDaemonOwnerNotice(p, message);
|
|
430
|
+
for (const owner of validOwners) {
|
|
431
|
+
try {
|
|
432
|
+
const result = await msgSend({
|
|
433
|
+
from: daemonAid,
|
|
434
|
+
to: owner,
|
|
435
|
+
body: { mode: 'text', text },
|
|
436
|
+
slotId: 'restart-monitor',
|
|
437
|
+
});
|
|
438
|
+
if (!result.ok) {
|
|
439
|
+
log(`Daemon owner notification failed for ${owner}: ${result.error}`);
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
log(`Daemon owner notification sent to ${owner}: ${result.message_id}`);
|
|
443
|
+
}
|
|
444
|
+
catch (error) {
|
|
445
|
+
log(`Daemon owner notification failed for ${owner}: ${error.message?.slice(0, 200) || error}`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
function formatDaemonOwnerNotice(p, message) {
|
|
450
|
+
return [
|
|
451
|
+
'EvolCore restart-monitor',
|
|
452
|
+
message,
|
|
453
|
+
'',
|
|
454
|
+
`EVOLCORE_HOME: ${p.root}`,
|
|
455
|
+
].join('\n');
|
|
456
|
+
}
|