evolcore 0.0.22 → 0.0.23
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/AGENTS.md +62 -0
- package/CHANGELOG.md +24 -14
- package/README.md +2 -0
- package/bin/codex-managed-acl.ps1 +155 -0
- package/bin/install-codex-managed-hooks.mjs +373 -95
- package/dist/agents/baseagent-availability.js +48 -0
- package/dist/agents/baseagent.js +1 -1
- package/dist/agents/claude-runner.js +30 -226
- package/dist/agents/{codex-app-server-client.js → codex/app-server-client.js} +73 -13
- package/dist/{utils/codex-app-server-registry.js → agents/codex/app-server-registry.js} +2 -2
- package/dist/agents/codex/catalog.js +70 -0
- package/dist/agents/codex/diagnostics.js +154 -0
- package/dist/agents/codex-runner.js +407 -139
- package/dist/agents/ecagent-runner.js +10 -31
- package/dist/agents/gemini-runner.js +2 -6
- package/dist/aun/aid/store.js +2 -0
- package/dist/aun/menu-client.js +272 -0
- package/dist/aun/msg/group.js +65 -4
- package/dist/aun/msg/index.js +1 -1
- package/dist/aun/msg/managed-operation.js +51 -4
- package/dist/aun/rpc/client.js +8 -1
- package/dist/channels/aun.js +31 -1
- package/dist/channels/wechat.js +81 -10
- package/dist/cli/aun-commands.js +48 -4
- package/dist/cli/command-log.js +21 -2
- package/dist/cli/daemon-commands.js +142 -45
- package/dist/cli/index.js +20 -0
- package/dist/cli/init.js +2 -10
- package/dist/cli/menu-client.js +95 -0
- package/dist/cli/model.js +23 -8
- package/dist/cli/task-context.js +12 -2
- package/dist/cli/trigger-command.js +9 -1
- package/dist/config/schema-migration.js +14 -2
- package/dist/core/auth/agent-delegation.js +10 -7
- package/dist/core/auth/authorization-audit.js +4 -0
- package/dist/core/auth/operation-catalog.js +4 -0
- package/dist/core/auth/remote-access-store.js +135 -0
- package/dist/core/command/command-handler.js +41 -5
- package/dist/core/command/daemon-diagnostics.js +448 -0
- package/dist/core/command/menu-catalog.js +3 -0
- package/dist/core/command/menu-handler.js +141 -33
- package/dist/core/command/menu-protocol.js +119 -0
- package/dist/core/command/slash-gate.js +3 -3
- package/dist/core/command/slash-handler.js +42 -22
- package/dist/core/message/message-queue.js +20 -15
- package/dist/core/message/response-engine.js +74 -73
- package/dist/core/model/catalog-service.js +308 -0
- package/dist/core/model/model-catalog.js +124 -334
- package/dist/core/model/model-diagnostics.js +4 -5
- package/dist/core/model/model-fallback.js +9 -16
- package/dist/core/model/model-permission.js +1 -1
- package/dist/core/permission/approval-gateway.js +16 -1
- package/dist/core/permission/protected-paths.js +63 -4
- package/dist/core/permission/sandbox-runtime.js +4 -2
- package/dist/core/permission/tool-policy.js +244 -25
- package/dist/core/runtime-lock.js +13 -8
- package/dist/index.js +40 -29
- package/dist/ipc.js +123 -81
- package/dist/trigger/manager.js +3 -1
- package/dist/trigger/parser.js +77 -13
- package/dist/trigger/patch.js +104 -1
- package/dist/utils/ecweb-supervisor.js +11 -2
- package/dist/utils/ecweb-utils.js +8 -26
- package/dist/utils/error-utils.js +7 -0
- package/dist/utils/npm-ops.js +47 -15
- package/kits/docs/evolcore/INDEX.md +1 -0
- package/kits/docs/evolcore/agent-create.md +13 -0
- package/kits/docs/evolcore/group-collaboration.md +7 -2
- package/kits/docs/evolcore/menu-client.md +34 -0
- package/kits/migrations/migrate-contact-book-v2.mjs +9 -1
- package/kits/migrations/migrate-role-config-v4.mjs +11 -5
- package/kits/migrations/migrate-role-config-v5.mjs +8 -2
- package/kits/templates/roles/visitor.json +1 -0
- package/kits/templates/system-fragments/bootstrap.md +8 -1
- package/package.json +11 -3
package/dist/index.js
CHANGED
|
@@ -45,7 +45,7 @@ import { ResponseEngine } from './core/message/response-engine.js';
|
|
|
45
45
|
import { MessageQueue } from './core/message/message-queue.js';
|
|
46
46
|
import { MessageBridge } from './core/message/message-bridge.js';
|
|
47
47
|
import { evaluateOutboundContactAdmission } from './core/message/message-admission.js';
|
|
48
|
-
import {
|
|
48
|
+
import { enforceMenuResponseSize, evaluateMenuClientCompatibility, evolMenuResponseTransportMetadata, MenuRequestDeduper, hasValidMenuId, menuFailure, menuPayloadFingerprint, normalizeMenuResponseTiming, parseMenuControl, validateMenuRequest, withMenuProcessingTime, logMenuRequestCompleted, logMenuRequestReceived } from './core/command/menu-protocol.js';
|
|
49
49
|
import { readInstalledEvolcoreVersion } from './utils/evolcore-version.js';
|
|
50
50
|
import { recoverAllRoleMutationsSync } from './core/command/role-menu.js';
|
|
51
51
|
import { HandoffRuntime } from './core/handoff/runtime.js';
|
|
@@ -74,7 +74,7 @@ import { AgentReloadBusyError, AgentReloadCoordinator } from './core/agent-reloa
|
|
|
74
74
|
import { buildReloadHooks } from './core/channel-loader.js';
|
|
75
75
|
import { IpcServer } from './ipc.js';
|
|
76
76
|
import { logger, setLogLevel } from './utils/logger.js';
|
|
77
|
-
import {
|
|
77
|
+
import { remoteAccessService } from './core/auth/remote-access-store.js';
|
|
78
78
|
import { createEcwebSupervisor } from './utils/ecweb-supervisor.js';
|
|
79
79
|
import { writeMain, removeAll, isMainWinner, scanInstances } from './utils/instance-registry.js';
|
|
80
80
|
import { detectDuplicates } from './core/evolagent-registry.js';
|
|
@@ -551,6 +551,7 @@ async function runBindBootstrapDaemon(daemonCfg) {
|
|
|
551
551
|
})(),
|
|
552
552
|
}));
|
|
553
553
|
ipcServer.setControlToken(controlToken);
|
|
554
|
+
ipcServer.setRemoteAccessService(remoteAccessService);
|
|
554
555
|
ipcServer.setShutdownExecutor(async (reason) => {
|
|
555
556
|
if (!shutdown) {
|
|
556
557
|
pendingShutdownReason = reason;
|
|
@@ -1354,6 +1355,7 @@ async function main() {
|
|
|
1354
1355
|
baseagent: primaryAgent?.baseagent || 'codex',
|
|
1355
1356
|
projectPath: primaryAgent?.projectPath || process.cwd(),
|
|
1356
1357
|
};
|
|
1358
|
+
cmdHandler.setDaemonTriggerOwner(daemonTriggerOwner.aid, daemonChannel.channelName);
|
|
1357
1359
|
const triggerBaseagentsForAgent = (agentAid) => {
|
|
1358
1360
|
const ownerName = agentRegistry.get(agentAid)?.name
|
|
1359
1361
|
?? (agentAid === daemonTriggerOwner.aid ? primaryAgent?.name : undefined);
|
|
@@ -2309,6 +2311,7 @@ async function main() {
|
|
|
2309
2311
|
return cmdHandler.handleCtl(cmd, sessionId, delegation.grant);
|
|
2310
2312
|
});
|
|
2311
2313
|
ipcServer.setDaemonRuntimeEpoch(agentDelegationRegistry.getRuntimeEpoch());
|
|
2314
|
+
cmdHandler.setDaemonMonitorProvider(() => ipcServer.getMonitorSnapshot().snapshot);
|
|
2312
2315
|
// Register every IPC executor/provider before exposing the endpoint. The
|
|
2313
2316
|
// function declaration is hoisted, while its invocation remains here so a
|
|
2314
2317
|
// failed bind still aborts before any AUN connection is attempted.
|
|
@@ -2378,10 +2381,10 @@ async function main() {
|
|
|
2378
2381
|
catch (e) {
|
|
2379
2382
|
logger.warn(`控制 AID 首连失败(后台自动重连,不影响 daemon 主流程): ${e?.message || e}`);
|
|
2380
2383
|
}
|
|
2381
|
-
// 控制 AID 接收 owner
|
|
2382
|
-
// 1. /pair —
|
|
2384
|
+
// 控制 AID 接收 daemon-owner 或有效远程访问 token 授权的指令:
|
|
2385
|
+
// 1. /pair — daemon 远程控制配对码(文本快路径)
|
|
2383
2386
|
// 2. menu.* JSON — 路由到 cmdHandler.execMenuForControl(进程级 + 全量权限)
|
|
2384
|
-
// 发送方身份由 AUN X.509
|
|
2387
|
+
// 发送方身份由 AUN X.509 证书链验证;非 Menu 文本仍只接受 daemon-owner。
|
|
2385
2388
|
controlChannel.onMessage(async (opts) => {
|
|
2386
2389
|
let activeMenuRequest;
|
|
2387
2390
|
let menuCompletionLogged = false;
|
|
@@ -2389,6 +2392,11 @@ async function main() {
|
|
|
2389
2392
|
source: 'control-aun',
|
|
2390
2393
|
selfAid: daemonCfg.aid,
|
|
2391
2394
|
messageId: opts.messageId,
|
|
2395
|
+
requesterAid: opts.peerId,
|
|
2396
|
+
authMethod: opts.protectedHeaders?.auth_method === 'remote-access-token' || opts.protectedHeaders?.auth_method === 'ecweb-token'
|
|
2397
|
+
? opts.protectedHeaders.auth_method
|
|
2398
|
+
: 'daemon-owner',
|
|
2399
|
+
clientName: opts.protectedHeaders?.client_name ?? 'evol',
|
|
2392
2400
|
};
|
|
2393
2401
|
try {
|
|
2394
2402
|
const menuTiming = normalizeMenuResponseTiming(opts);
|
|
@@ -2402,7 +2410,7 @@ async function main() {
|
|
|
2402
2410
|
}
|
|
2403
2411
|
const menuResponseContext = () => controlReplyContext(opts, evolMenuResponseTransportMetadata());
|
|
2404
2412
|
const sendControlMenuResponse = async (response) => {
|
|
2405
|
-
const timedResponse = withMenuProcessingTime(response, menuTiming);
|
|
2413
|
+
const timedResponse = withMenuProcessingTime(enforceMenuResponseSize(response, 128 * 1024 - 1024), menuTiming);
|
|
2406
2414
|
const request = activeMenuRequest ?? {
|
|
2407
2415
|
type: 'menu.unknown',
|
|
2408
2416
|
id: response.id,
|
|
@@ -2450,7 +2458,21 @@ async function main() {
|
|
|
2450
2458
|
menuCompletionLogged = true;
|
|
2451
2459
|
return;
|
|
2452
2460
|
}
|
|
2453
|
-
|
|
2461
|
+
const menuClientCompatibilityError = menuControl.isMenu
|
|
2462
|
+
? evaluateMenuClientCompatibility({ encrypted: opts.encrypted, protectedHeaders: opts.protectedHeaders })
|
|
2463
|
+
: null;
|
|
2464
|
+
if (menuControl.isMenu && menuClientCompatibilityError) {
|
|
2465
|
+
await sendControlMenuResponse(menuFailure({ id: menuControl.id, ...(menuControl.name?.trim() ? { name: menuControl.name } : {}) }, menuClientCompatibilityError));
|
|
2466
|
+
return;
|
|
2467
|
+
}
|
|
2468
|
+
const protectedMenuHeaders = opts.protectedHeaders;
|
|
2469
|
+
const hasRemoteAccessToken = protectedMenuHeaders?.auth_method === 'remote-access-token'
|
|
2470
|
+
|| protectedMenuHeaders?.auth_method === 'ecweb-token';
|
|
2471
|
+
if (text.toLowerCase() === '/pair' && !isProcessLevelOwner(opts.peerId, currentProcessOwners)) {
|
|
2472
|
+
logger.debug(`控制 AID 收到非 owner 配对请求,忽略: from=${opts.peerId}`);
|
|
2473
|
+
return;
|
|
2474
|
+
}
|
|
2475
|
+
if (menuControl.isMenu && !isRoleMenu && !hasRemoteAccessToken && !isProcessLevelOwner(opts.peerId, currentProcessOwners)) {
|
|
2454
2476
|
const response = menuFailure({ id: menuControl.id, ...(menuControl.name?.trim() ? { name: menuControl.name } : {}) }, {
|
|
2455
2477
|
code: 'NOT_ALLOWED',
|
|
2456
2478
|
message: formatAuthorizationDenial('Control-channel process operations require DaemonOwner.', 'DAEMON_OWNER_REQUIRED'),
|
|
@@ -2459,40 +2481,28 @@ async function main() {
|
|
|
2459
2481
|
await sendControlMenuResponse(response);
|
|
2460
2482
|
return;
|
|
2461
2483
|
}
|
|
2462
|
-
if (!isRoleMenu && !isProcessLevelOwner(opts.peerId, currentProcessOwners)) {
|
|
2484
|
+
if (!isRoleMenu && !hasRemoteAccessToken && !isProcessLevelOwner(opts.peerId, currentProcessOwners)) {
|
|
2463
2485
|
logger.debug(`控制 AID 收到非 owner 消息,忽略: from=${opts.peerId}`);
|
|
2464
2486
|
return;
|
|
2465
2487
|
}
|
|
2466
2488
|
if (text.toLowerCase() === '/pair') {
|
|
2467
|
-
const
|
|
2468
|
-
const
|
|
2469
|
-
|
|
2470
|
-
if (pair) {
|
|
2471
|
-
const mins = Math.max(0, Math.round((pair.expiresAt - Date.now()) / 60000));
|
|
2472
|
-
reply = `ECWeb 配对码:${pair.code}(约 ${mins} 分钟内有效)\n在浏览器打开 ECWeb 后输入此码登录`;
|
|
2473
|
-
}
|
|
2474
|
-
else {
|
|
2475
|
-
reply = 'ECWeb 未运行或暂不可达。请在主机运行 ec watch web 启动后重试。';
|
|
2476
|
-
}
|
|
2489
|
+
const pair = remoteAccessService.getPairingCode(opts.peerId);
|
|
2490
|
+
const mins = Math.max(0, Math.round((pair.expiresAt - Date.now()) / 60000));
|
|
2491
|
+
const reply = `远程控制配对码:${pair.code}(约 ${mins} 分钟内有效)`;
|
|
2477
2492
|
await controlChannel.sendMessage(opts.channelId, reply, controlReplyContext(opts));
|
|
2478
2493
|
return;
|
|
2479
2494
|
}
|
|
2480
|
-
// menu.* JSON
|
|
2495
|
+
// menu.* JSON 路由:准入来源在上方分流,最终 token/owner 校验由 execMenuForControl 完成。
|
|
2481
2496
|
if (menuControl.isMenu) {
|
|
2482
|
-
const versionError = evaluateEvolMenuVersionGate({
|
|
2483
|
-
encrypted: opts.encrypted,
|
|
2484
|
-
protectedHeaders: opts.protectedHeaders,
|
|
2485
|
-
});
|
|
2486
|
-
if (versionError) {
|
|
2487
|
-
await sendControlMenuResponse(menuFailure({ id: menuControl.id, ...(menuControl.name?.trim() ? { name: menuControl.name } : {}) }, versionError));
|
|
2488
|
-
return;
|
|
2489
|
-
}
|
|
2490
2497
|
const validationError = validateMenuRequest(menuControl.request);
|
|
2491
2498
|
if (validationError) {
|
|
2492
2499
|
await sendControlMenuResponse(menuFailure({ id: menuControl.id, ...(menuControl.name?.trim() ? { name: menuControl.name } : {}) }, validationError));
|
|
2493
2500
|
return;
|
|
2494
2501
|
}
|
|
2495
|
-
const deduped = await controlMenuDeduper.execute([opts.channelId, opts.peerId, menuControl.id].join('\u001f'), menuPayloadFingerprint(menuControl.raw), () => cmdHandler.execMenuForControl(parsed, opts.peerId
|
|
2502
|
+
const deduped = await controlMenuDeduper.execute([opts.channelId, opts.peerId, menuControl.id].join('\u001f'), menuPayloadFingerprint(menuControl.raw), () => cmdHandler.execMenuForControl(parsed, opts.peerId, {
|
|
2503
|
+
encrypted: opts.encrypted,
|
|
2504
|
+
protectedHeaders: opts.protectedHeaders,
|
|
2505
|
+
}));
|
|
2496
2506
|
if ('retry' in deduped) {
|
|
2497
2507
|
if (deduped.replayed) {
|
|
2498
2508
|
logger.warn(`[MenuControl] category=request-replay request=${menuControl.id}`
|
|
@@ -2512,7 +2522,7 @@ async function main() {
|
|
|
2512
2522
|
return;
|
|
2513
2523
|
}
|
|
2514
2524
|
// owner 发的其他内容:提示可用指令
|
|
2515
|
-
await controlChannel.sendMessage(opts.channelId, '可用指令:/pair
|
|
2525
|
+
await controlChannel.sendMessage(opts.channelId, '可用指令:/pair(获取远程控制配对码)', controlReplyContext(opts));
|
|
2516
2526
|
}
|
|
2517
2527
|
catch (e) {
|
|
2518
2528
|
if (activeMenuRequest && !menuCompletionLogged) {
|
|
@@ -2688,6 +2698,7 @@ async function main() {
|
|
|
2688
2698
|
// M3: direct call (not cast) — wire EvolAgentRegistry into IPC for evolagent.* handlers
|
|
2689
2699
|
ipcServer.setAgentRegistry(agentRegistry);
|
|
2690
2700
|
ipcServer.setControlToken(triggerControlToken);
|
|
2701
|
+
ipcServer.setRemoteAccessService(remoteAccessService);
|
|
2691
2702
|
ipcServer.setBootstrapCompleteExecutor(async (aid, auth) => {
|
|
2692
2703
|
if (auth?.sessionId) {
|
|
2693
2704
|
const authorization = await authorizeManagedAgentOperation({
|
package/dist/ipc.js
CHANGED
|
@@ -17,6 +17,8 @@ import { classifyProcessTree, ProcessTreeSampler, readProcessMetrics } from './u
|
|
|
17
17
|
import { readSystemMemoryUsage } from './utils/system-memory.js';
|
|
18
18
|
import { hashCurrentDelegatedCommand } from './core/auth/agent-delegation.js';
|
|
19
19
|
import { normalizeAunMentionEntries } from './aun/msg/mention-schema.js';
|
|
20
|
+
import { remoteAccessService } from './core/auth/remote-access-store.js';
|
|
21
|
+
import { setModelCatalogDaemonOwner } from './core/model/model-catalog.js';
|
|
20
22
|
const isWindows = process.platform === 'win32';
|
|
21
23
|
const isNamedPipe = (p) => isWindows && p.toLowerCase().startsWith('\\\\.\\pipe\\');
|
|
22
24
|
const CPU_SAMPLE_INTERVAL_MS = 1000;
|
|
@@ -44,10 +46,15 @@ const LOCAL_CONTROL_COMMANDS = new Set([
|
|
|
44
46
|
'evolagent.load',
|
|
45
47
|
'evolagent.resync',
|
|
46
48
|
'menu.exec',
|
|
49
|
+
'model.catalog',
|
|
47
50
|
'monitor-snapshot',
|
|
48
51
|
'monitor-subscribe',
|
|
49
52
|
'monitor-unsubscribe',
|
|
50
53
|
'shutdown',
|
|
54
|
+
'remote-auth.pair-code',
|
|
55
|
+
'remote-auth.issue-local',
|
|
56
|
+
'remote-auth.redeem',
|
|
57
|
+
'remote-auth.validate',
|
|
51
58
|
]);
|
|
52
59
|
let ipcQuerySequence = 0;
|
|
53
60
|
function ipcLogToken(value, fallback = 'unknown') {
|
|
@@ -127,6 +134,7 @@ export class IpcServer {
|
|
|
127
134
|
wechatContactBindExecutor;
|
|
128
135
|
controlToken;
|
|
129
136
|
daemonRuntimeEpoch;
|
|
137
|
+
remoteAccessService = remoteAccessService;
|
|
130
138
|
// CPU 占用追踪:IPC handler 是一次性同步调用,无法在响应里做 200ms 异步采样,
|
|
131
139
|
// 故用后台 1s interval 累积 process.cpuUsage() 增量,handler 直接读最近值。
|
|
132
140
|
// procCpuPercent = 本 daemon 进程占单核的百分比(可 >100% 仅当多核,已 clamp 到 100);
|
|
@@ -177,6 +185,10 @@ export class IpcServer {
|
|
|
177
185
|
setControlToken(token) {
|
|
178
186
|
this.controlToken = Buffer.from(token);
|
|
179
187
|
}
|
|
188
|
+
/** Use the daemon-owned remote access service for local consumers such as ECWeb. */
|
|
189
|
+
setRemoteAccessService(service) {
|
|
190
|
+
this.remoteAccessService = service;
|
|
191
|
+
}
|
|
180
192
|
/** Set the process-lifetime marker used to reject stale managed children. */
|
|
181
193
|
setDaemonRuntimeEpoch(epoch) {
|
|
182
194
|
this.daemonRuntimeEpoch = epoch;
|
|
@@ -233,6 +245,87 @@ export class IpcServer {
|
|
|
233
245
|
setAgentStatsProvider(provider) {
|
|
234
246
|
this.agentStatsProvider = provider;
|
|
235
247
|
}
|
|
248
|
+
/** Single current snapshot shared by ECWeb IPC and the daemon Menu view. */
|
|
249
|
+
getMonitorSnapshot(leaseId) {
|
|
250
|
+
if (typeof leaseId === 'string' && leaseId.trim())
|
|
251
|
+
this.refreshProcessTreeMonitor(leaseId);
|
|
252
|
+
else
|
|
253
|
+
this.sampleProcessTree();
|
|
254
|
+
const mem = process.memoryUsage();
|
|
255
|
+
const systemMemory = readSystemMemoryUsage();
|
|
256
|
+
const aids = this.aunAidProvider ? this.aunAidProvider() : [];
|
|
257
|
+
const aidStats = this.aunAidStatsProvider ? this.aunAidStatsProvider() : [];
|
|
258
|
+
const statsMap = new Map(aidStats.map(stat => [stat.aid, stat]));
|
|
259
|
+
const agentInfos = this.agentRegistry ? this.agentRegistry.list() : [];
|
|
260
|
+
const agentRuntimeStats = this.agentStatsProvider ? this.agentStatsProvider() : [];
|
|
261
|
+
const agentRuntimeStatsMap = new Map(agentRuntimeStats.map(stat => [stat.aid, stat]));
|
|
262
|
+
const aidStateMap = new Map(aids.map(state => [state.agentName || state.aid, state]));
|
|
263
|
+
const processTree = this.processTreeSnapshot;
|
|
264
|
+
const processTreeCpu = processTree?.processCount ? processTree.cpuPercent : this.cpuPercent;
|
|
265
|
+
const processTreeRss = processTree?.processCount ? processTree.rss : mem.rss;
|
|
266
|
+
const processTreeDetails = processTree ? {
|
|
267
|
+
...processTree,
|
|
268
|
+
processes: classifyProcessTree(processTree.processes, process.pid, this.associatedProcessKinds),
|
|
269
|
+
} : {
|
|
270
|
+
rootPid: process.pid,
|
|
271
|
+
rootPids: [process.pid],
|
|
272
|
+
processCount: 1,
|
|
273
|
+
cpuPercent: Math.round(this.cpuPercent * 10) / 10,
|
|
274
|
+
rss: mem.rss,
|
|
275
|
+
processes: [{
|
|
276
|
+
pid: process.pid,
|
|
277
|
+
ppid: process.ppid,
|
|
278
|
+
name: 'evolcore',
|
|
279
|
+
cpuPercent: Math.round(this.cpuPercent * 10) / 10,
|
|
280
|
+
rss: mem.rss,
|
|
281
|
+
kind: 'daemon',
|
|
282
|
+
}],
|
|
283
|
+
};
|
|
284
|
+
return {
|
|
285
|
+
ok: true,
|
|
286
|
+
snapshot: {
|
|
287
|
+
ts: Date.now(),
|
|
288
|
+
uptimeMs: Math.round(process.uptime() * 1000),
|
|
289
|
+
cpuCount: os.cpus().length,
|
|
290
|
+
memory: {
|
|
291
|
+
rss: processTreeRss,
|
|
292
|
+
heapUsed: mem.heapUsed,
|
|
293
|
+
heapTotal: mem.heapTotal,
|
|
294
|
+
external: mem.external,
|
|
295
|
+
},
|
|
296
|
+
cpuPercent: processTreeCpu,
|
|
297
|
+
mainProcess: {
|
|
298
|
+
pid: process.pid,
|
|
299
|
+
cpuPercent: Math.round(this.cpuPercent * 10) / 10,
|
|
300
|
+
memory: mem,
|
|
301
|
+
},
|
|
302
|
+
processTree: processTreeDetails,
|
|
303
|
+
sampling: {
|
|
304
|
+
cpuIntervalMs: CPU_SAMPLE_INTERVAL_MS,
|
|
305
|
+
processTreeIntervalMs: PROCESS_TREE_SAMPLE_INTERVAL_MS,
|
|
306
|
+
eventLoopLagMs: this.eventLoopLagMs,
|
|
307
|
+
maxEventLoopLagMs: this.maxEventLoopLagMs,
|
|
308
|
+
processTree: { ...this.processTreeSampling },
|
|
309
|
+
},
|
|
310
|
+
system: {
|
|
311
|
+
...systemMemory,
|
|
312
|
+
cpuPercent: Math.round(this.sysCpuPercent * 10) / 10,
|
|
313
|
+
loadAvg: os.loadavg(),
|
|
314
|
+
},
|
|
315
|
+
stats: this.statsProvider ? this.statsProvider() : null,
|
|
316
|
+
agentSummary: summarizeMonitorAgents(agentInfos, aids),
|
|
317
|
+
agents: agentInfos.map(agent => ({
|
|
318
|
+
aid: agent.aid,
|
|
319
|
+
agentName: agent.name,
|
|
320
|
+
status: agent.status,
|
|
321
|
+
connectionStatus: aidStateMap.get(agent.aid)?.status ?? (agent.status === 'disabled' ? 'disabled' : 'unavailable'),
|
|
322
|
+
online: agent.status !== 'disabled' && aidStateMap.get(agent.aid)?.status === 'connected',
|
|
323
|
+
stats: statsMap.get(agent.aid) ?? null,
|
|
324
|
+
runtimeStats: agentRuntimeStatsMap.get(agent.aid) ?? null,
|
|
325
|
+
})),
|
|
326
|
+
},
|
|
327
|
+
};
|
|
328
|
+
}
|
|
236
329
|
/** Inject queue snapshot provider for queue-snapshot IPC handler (query mode) */
|
|
237
330
|
setQueueSnapshotProvider(provider) {
|
|
238
331
|
this.queueSnapshotProvider = provider;
|
|
@@ -623,6 +716,7 @@ export class IpcServer {
|
|
|
623
716
|
return roots;
|
|
624
717
|
}
|
|
625
718
|
start() {
|
|
719
|
+
setModelCatalogDaemonOwner(true);
|
|
626
720
|
if (this.server)
|
|
627
721
|
return Promise.resolve();
|
|
628
722
|
// Remove stale socket file (Unix only — named pipes auto-cleanup on process exit)
|
|
@@ -685,6 +779,7 @@ export class IpcServer {
|
|
|
685
779
|
});
|
|
686
780
|
}
|
|
687
781
|
stop() {
|
|
782
|
+
setModelCatalogDaemonOwner(false);
|
|
688
783
|
if (this.server) {
|
|
689
784
|
this.server.close();
|
|
690
785
|
this.server = null;
|
|
@@ -712,6 +807,19 @@ export class IpcServer {
|
|
|
712
807
|
};
|
|
713
808
|
}
|
|
714
809
|
switch (cmd.type) {
|
|
810
|
+
case 'model.catalog': {
|
|
811
|
+
if ((cmd.self !== undefined && typeof cmd.self !== 'string')
|
|
812
|
+
|| (cmd.baseagent !== undefined && !['claude', 'codex', 'ecagent', 'gemini'].includes(cmd.baseagent))) {
|
|
813
|
+
return { ok: false, error: 'Invalid model catalog scope' };
|
|
814
|
+
}
|
|
815
|
+
try {
|
|
816
|
+
const { getLocalCatalog } = await import('./core/model/model-catalog.js');
|
|
817
|
+
return { ok: true, snapshot: await getLocalCatalog(cmd.self, cmd.baseagent, cmd.refresh === true) };
|
|
818
|
+
}
|
|
819
|
+
catch {
|
|
820
|
+
return { ok: false, error: 'Unable to resolve model catalog configuration' };
|
|
821
|
+
}
|
|
822
|
+
}
|
|
715
823
|
case 'status':
|
|
716
824
|
return this.getStatus();
|
|
717
825
|
case 'codex-pre-tool-use': {
|
|
@@ -769,6 +877,19 @@ export class IpcServer {
|
|
|
769
877
|
ttlMs: cmd.ttlMs,
|
|
770
878
|
});
|
|
771
879
|
}
|
|
880
|
+
case 'remote-auth.pair-code': {
|
|
881
|
+
const pair = this.remoteAccessService.getPairingCode(typeof cmd.authorizedBy === 'string' ? cmd.authorizedBy : undefined);
|
|
882
|
+
return { ok: true, ...pair };
|
|
883
|
+
}
|
|
884
|
+
case 'remote-auth.issue-local': {
|
|
885
|
+
return this.remoteAccessService.issueLocalToken(cmd.label);
|
|
886
|
+
}
|
|
887
|
+
case 'remote-auth.redeem': {
|
|
888
|
+
return this.remoteAccessService.redeemPairingCode(cmd.code, cmd.label, cmd.authorizedBy);
|
|
889
|
+
}
|
|
890
|
+
case 'remote-auth.validate': {
|
|
891
|
+
return { ok: true, ...this.remoteAccessService.validateToken(cmd.token) };
|
|
892
|
+
}
|
|
772
893
|
case 'bind.status': {
|
|
773
894
|
if (!this.bindExecutor)
|
|
774
895
|
return { ok: false, error: 'bind executor not configured' };
|
|
@@ -1385,87 +1506,8 @@ export class IpcServer {
|
|
|
1385
1506
|
return { ok: false, error: e?.message ?? String(e) };
|
|
1386
1507
|
}
|
|
1387
1508
|
}
|
|
1388
|
-
case 'monitor-snapshot':
|
|
1389
|
-
this.
|
|
1390
|
-
// watch web Monitor 页用:进程级 + 系统级运行指标 + 全局 stats + per-agent 汇总。
|
|
1391
|
-
const mem = process.memoryUsage();
|
|
1392
|
-
const systemMemory = readSystemMemoryUsage();
|
|
1393
|
-
const aids = this.aunAidProvider ? this.aunAidProvider() : [];
|
|
1394
|
-
const aidStats = this.aunAidStatsProvider ? this.aunAidStatsProvider() : [];
|
|
1395
|
-
const statsMap = new Map(aidStats.map((s) => [s.aid, s]));
|
|
1396
|
-
const agentInfos = this.agentRegistry ? this.agentRegistry.list() : [];
|
|
1397
|
-
const agentRuntimeStats = this.agentStatsProvider ? this.agentStatsProvider() : [];
|
|
1398
|
-
const agentRuntimeStatsMap = new Map(agentRuntimeStats.map((s) => [s.aid, s]));
|
|
1399
|
-
const aidStateMap = new Map(aids.map((state) => [state.agentName || state.aid, state]));
|
|
1400
|
-
const agentSummary = summarizeMonitorAgents(agentInfos, aids);
|
|
1401
|
-
const processTree = this.processTreeSnapshot;
|
|
1402
|
-
const processTreeCpu = processTree?.processCount ? processTree.cpuPercent : this.cpuPercent;
|
|
1403
|
-
const processTreeRss = processTree?.processCount ? processTree.rss : mem.rss;
|
|
1404
|
-
const processTreeDetails = processTree ? {
|
|
1405
|
-
...processTree,
|
|
1406
|
-
processes: classifyProcessTree(processTree.processes, process.pid, this.associatedProcessKinds),
|
|
1407
|
-
} : {
|
|
1408
|
-
rootPid: process.pid,
|
|
1409
|
-
rootPids: [process.pid],
|
|
1410
|
-
processCount: 1,
|
|
1411
|
-
cpuPercent: Math.round(this.cpuPercent * 10) / 10,
|
|
1412
|
-
rss: mem.rss,
|
|
1413
|
-
processes: [{
|
|
1414
|
-
pid: process.pid,
|
|
1415
|
-
ppid: process.ppid,
|
|
1416
|
-
name: 'evolcore',
|
|
1417
|
-
cpuPercent: Math.round(this.cpuPercent * 10) / 10,
|
|
1418
|
-
rss: mem.rss,
|
|
1419
|
-
kind: 'daemon',
|
|
1420
|
-
}],
|
|
1421
|
-
};
|
|
1422
|
-
return {
|
|
1423
|
-
ok: true,
|
|
1424
|
-
snapshot: {
|
|
1425
|
-
ts: Date.now(),
|
|
1426
|
-
uptimeMs: Math.round(process.uptime() * 1000),
|
|
1427
|
-
cpuCount: os.cpus().length,
|
|
1428
|
-
// 进程级:daemon + 子进程 + 登记在册的关联进程树
|
|
1429
|
-
memory: {
|
|
1430
|
-
rss: processTreeRss,
|
|
1431
|
-
heapUsed: mem.heapUsed,
|
|
1432
|
-
heapTotal: mem.heapTotal,
|
|
1433
|
-
external: mem.external,
|
|
1434
|
-
},
|
|
1435
|
-
cpuPercent: processTreeCpu,
|
|
1436
|
-
mainProcess: {
|
|
1437
|
-
pid: process.pid,
|
|
1438
|
-
cpuPercent: Math.round(this.cpuPercent * 10) / 10,
|
|
1439
|
-
memory: mem,
|
|
1440
|
-
},
|
|
1441
|
-
processTree: processTreeDetails,
|
|
1442
|
-
sampling: {
|
|
1443
|
-
cpuIntervalMs: CPU_SAMPLE_INTERVAL_MS,
|
|
1444
|
-
processTreeIntervalMs: PROCESS_TREE_SAMPLE_INTERVAL_MS,
|
|
1445
|
-
eventLoopLagMs: this.eventLoopLagMs,
|
|
1446
|
-
maxEventLoopLagMs: this.maxEventLoopLagMs,
|
|
1447
|
-
processTree: { ...this.processTreeSampling },
|
|
1448
|
-
},
|
|
1449
|
-
// 系统级:整机
|
|
1450
|
-
system: {
|
|
1451
|
-
...systemMemory,
|
|
1452
|
-
cpuPercent: Math.round(this.sysCpuPercent * 10) / 10,
|
|
1453
|
-
loadAvg: os.loadavg(), // [1m, 5m, 15m](Windows 恒 0)
|
|
1454
|
-
},
|
|
1455
|
-
stats: this.statsProvider ? this.statsProvider() : null,
|
|
1456
|
-
agentSummary,
|
|
1457
|
-
agents: agentInfos.map((a) => ({
|
|
1458
|
-
aid: a.aid,
|
|
1459
|
-
agentName: a.name,
|
|
1460
|
-
status: a.status,
|
|
1461
|
-
connectionStatus: aidStateMap.get(a.aid)?.status ?? (a.status === 'disabled' ? 'disabled' : 'unavailable'),
|
|
1462
|
-
online: a.status !== 'disabled' && aidStateMap.get(a.aid)?.status === 'connected',
|
|
1463
|
-
stats: statsMap.get(a.aid) ?? null,
|
|
1464
|
-
runtimeStats: agentRuntimeStatsMap.get(a.aid) ?? null,
|
|
1465
|
-
})),
|
|
1466
|
-
},
|
|
1467
|
-
};
|
|
1468
|
-
}
|
|
1509
|
+
case 'monitor-snapshot':
|
|
1510
|
+
return this.getMonitorSnapshot(cmd.leaseId);
|
|
1469
1511
|
case 'monitor-subscribe': {
|
|
1470
1512
|
const leaseId = typeof cmd.leaseId === 'string' ? cmd.leaseId.trim() : '';
|
|
1471
1513
|
if (!leaseId || leaseId.length > 128)
|
package/dist/trigger/manager.js
CHANGED
|
@@ -92,7 +92,9 @@ export class TriggerDefinitionManager {
|
|
|
92
92
|
assertExplicitAunDeliveryRoutes(updated);
|
|
93
93
|
this.assertAgent(updated);
|
|
94
94
|
if (existing.execution.baseagent !== updated.execution.baseagent) {
|
|
95
|
-
|
|
95
|
+
if (existing.execution.type !== 'trigger_session' || updated.execution.type !== 'trigger_session') {
|
|
96
|
+
throw new Error('execution.baseagent is only mutable between trigger_session Triggers');
|
|
97
|
+
}
|
|
96
98
|
}
|
|
97
99
|
updated.createdAt = existing.createdAt;
|
|
98
100
|
updated.updatedAt = Date.now();
|
package/dist/trigger/parser.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { CronExpressionParser } from 'cron-parser';
|
|
2
2
|
const TRIGGER_UPDATE_FLAGS = new Set([
|
|
3
3
|
'once', 'delay', 'at', 'cron', 'every', 'event', 'tz',
|
|
4
|
-
'prompt', 'name',
|
|
5
|
-
'script-file',
|
|
4
|
+
'prompt', 'name', 'description',
|
|
5
|
+
'script-file', 'script-runtime', 'script-args', 'script-timeout',
|
|
6
|
+
'feedback', 'target-channel', 'target-channel-id', 'target-chat-type', 'target-session', 'target-thread-id',
|
|
7
|
+
'trigger-thread', 'baseagent',
|
|
8
|
+
'on-error', 'noop-sentinel', 'retry-attempts', 'retry-backoff',
|
|
6
9
|
'model', 'effort', 'permission',
|
|
7
10
|
'max-runs', 'max-duration', 'concurrency', 'missed-policy',
|
|
8
11
|
]);
|
|
@@ -396,18 +399,22 @@ function commonParsed(flags, opts = {}) {
|
|
|
396
399
|
if (!opts.update && !execution.value) {
|
|
397
400
|
return { ok: false, error: '创建 trigger 必须显式指定 --exec script|trigger-session|target-session' };
|
|
398
401
|
}
|
|
402
|
+
// update 场景下执行类型未知(不重命名 --exec),execution-type 相关校验
|
|
403
|
+
// 全部交给 patch/validation 按现有定义执行。
|
|
399
404
|
const inferredExecution = execution.value;
|
|
400
|
-
if (
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
if (
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
405
|
+
if (!opts.update) {
|
|
406
|
+
if (baseagent.value && inferredExecution !== 'trigger_session') {
|
|
407
|
+
return { ok: false, error: '--baseagent 仅适用于 --exec trigger-session' };
|
|
408
|
+
}
|
|
409
|
+
if (inferredExecution === 'script') {
|
|
410
|
+
if (!scriptPath)
|
|
411
|
+
return { ok: false, error: '--exec script 需要 --script-path' };
|
|
412
|
+
if (scriptPath && !scriptRuntime)
|
|
413
|
+
return { ok: false, error: '--script-path 必须配合 --script-runtime 使用(node|python|bash)' };
|
|
414
|
+
}
|
|
415
|
+
else if (!prompt) {
|
|
416
|
+
return { ok: false, error: '--prompt 为必填项' };
|
|
417
|
+
}
|
|
411
418
|
}
|
|
412
419
|
const targetSessionValue = targetSession.value ?? (targetThreadId ? 'thread' : 'main');
|
|
413
420
|
const feedbackStrategy = feedback.value ?? (hasTargetField ? 'target' : 'origin');
|
|
@@ -645,6 +652,63 @@ function parseTriggerUpdateFlags(nameOrId, flags, opts = {}) {
|
|
|
645
652
|
}
|
|
646
653
|
update.missedPolicy = normalized;
|
|
647
654
|
}
|
|
655
|
+
const description = optionalFlagString(flags, 'description');
|
|
656
|
+
if ('error' in description)
|
|
657
|
+
return { ok: false, error: description.error };
|
|
658
|
+
if (description.value !== undefined)
|
|
659
|
+
update.description = description.value || null;
|
|
660
|
+
if (flags.has('feedback') && value.feedbackStrategy !== undefined)
|
|
661
|
+
update.feedback = value.feedbackStrategy;
|
|
662
|
+
if (value.targetChannel !== undefined)
|
|
663
|
+
update.targetChannel = value.targetChannel;
|
|
664
|
+
if (value.targetChannelId !== undefined)
|
|
665
|
+
update.targetChannelId = value.targetChannelId;
|
|
666
|
+
if (value.targetChatType !== undefined)
|
|
667
|
+
update.targetChatType = value.targetChatType;
|
|
668
|
+
if (value.targetSession !== undefined && flags.has('target-session'))
|
|
669
|
+
update.targetSession = value.targetSession;
|
|
670
|
+
if (value.targetThreadId !== undefined)
|
|
671
|
+
update.targetThreadId = value.targetThreadId;
|
|
672
|
+
if (value.triggerThread !== undefined)
|
|
673
|
+
update.triggerThread = value.triggerThread;
|
|
674
|
+
if (value.baseagent !== undefined)
|
|
675
|
+
update.baseagent = value.baseagent;
|
|
676
|
+
const onError = optionalFlagString(flags, 'on-error');
|
|
677
|
+
if ('error' in onError)
|
|
678
|
+
return { ok: false, error: onError.error };
|
|
679
|
+
if (onError.value !== undefined) {
|
|
680
|
+
if (onError.value !== 'fail' && onError.value !== 'retry') {
|
|
681
|
+
return { ok: false, error: '--on-error 只接受 fail 或 retry' };
|
|
682
|
+
}
|
|
683
|
+
update.onError = onError.value;
|
|
684
|
+
}
|
|
685
|
+
const noopSentinel = optionalFlagString(flags, 'noop-sentinel');
|
|
686
|
+
if ('error' in noopSentinel)
|
|
687
|
+
return { ok: false, error: noopSentinel.error };
|
|
688
|
+
if (noopSentinel.value !== undefined)
|
|
689
|
+
update.noopSentinel = noopSentinel.value || null;
|
|
690
|
+
const retryAttempts = optionalFlagString(flags, 'retry-attempts');
|
|
691
|
+
if ('error' in retryAttempts)
|
|
692
|
+
return { ok: false, error: retryAttempts.error };
|
|
693
|
+
if (retryAttempts.value !== undefined) {
|
|
694
|
+
if (!/^\d+$/.test(retryAttempts.value))
|
|
695
|
+
return { ok: false, error: '--retry-attempts 必须是非负整数' };
|
|
696
|
+
update.retryAttempts = Number(retryAttempts.value);
|
|
697
|
+
}
|
|
698
|
+
const retryBackoff = optionalFlagString(flags, 'retry-backoff');
|
|
699
|
+
if ('error' in retryBackoff)
|
|
700
|
+
return { ok: false, error: retryBackoff.error };
|
|
701
|
+
if (retryBackoff.value !== undefined) {
|
|
702
|
+
if (!/^\d+$/.test(retryBackoff.value))
|
|
703
|
+
return { ok: false, error: '--retry-backoff 必须是毫秒正整数' };
|
|
704
|
+
update.retryBackoff = Number(retryBackoff.value);
|
|
705
|
+
}
|
|
706
|
+
if (value.scriptRuntime !== undefined)
|
|
707
|
+
update.scriptRuntime = value.scriptRuntime;
|
|
708
|
+
if (value.scriptArgs !== undefined)
|
|
709
|
+
update.scriptArgs = value.scriptArgs;
|
|
710
|
+
if (value.scriptTimeoutMs !== undefined)
|
|
711
|
+
update.scriptTimeout = value.scriptTimeoutMs;
|
|
648
712
|
if (Object.keys(update).length === 0)
|
|
649
713
|
return { ok: false, error: '没有可更新的 trigger 参数' };
|
|
650
714
|
return { ok: true, nameOrId, value: update };
|