evolcore 0.0.22 → 0.0.24
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 +74 -14
- package/README.md +4 -0
- package/bin/codex-managed-acl.ps1 +160 -0
- package/bin/codex-managed-hook.mjs +127 -10
- package/bin/ec-safe-output.js +85 -3
- package/bin/install-codex-managed-hooks.mjs +384 -95
- package/dist/agents/baseagent-availability.js +48 -0
- package/dist/agents/baseagent.js +30 -2
- package/dist/agents/claude-runner.js +179 -240
- package/dist/agents/{codex-app-server-client.js → codex/app-server-client.js} +90 -20
- 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 +369 -0
- package/dist/agents/codex-runner.js +839 -192
- package/dist/agents/ecagent-runner.js +27 -34
- package/dist/agents/gemini-runner.js +62 -8
- package/dist/agents/runner-types.js +1 -1
- package/dist/aun/aid/managed-operation.js +28 -6
- package/dist/aun/aid/store.js +43 -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 +57 -5
- package/dist/aun/outbox.js +56 -18
- package/dist/aun/rpc/client.js +7 -1
- package/dist/channels/aun.js +178 -77
- package/dist/channels/daemon.js +7 -1
- package/dist/channels/dingtalk.js +1 -0
- package/dist/channels/feishu.js +10 -4
- package/dist/channels/wechat.js +81 -10
- package/dist/channels/wecom.js +2 -1
- package/dist/cli/agent-command.js +29 -3
- package/dist/cli/agent-create.js +538 -0
- package/dist/cli/agent.js +26 -675
- package/dist/cli/aun-commands.js +108 -24
- package/dist/cli/command-log.js +21 -2
- package/dist/cli/ctl-command.js +2 -0
- package/dist/cli/daemon-commands.js +289 -61
- package/dist/cli/index.js +22 -2
- package/dist/cli/init.js +2 -10
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/menu-client.js +95 -0
- package/dist/cli/model.js +23 -8
- package/dist/cli/restart-monitor.js +432 -8
- package/dist/cli/stats.js +110 -0
- package/dist/cli/task-context.js +12 -2
- package/dist/cli/trigger-command.js +9 -1
- package/dist/config/builtin-roles.js +2 -0
- package/dist/config/schema-migration.js +14 -2
- package/dist/core/auth/agent-delegation.js +10 -7
- package/dist/core/auth/auth-gateway.js +3 -2
- package/dist/core/auth/authorization-audit.js +75 -18
- package/dist/core/auth/operation-authorizer.js +63 -3
- package/dist/core/auth/operation-catalog.js +14 -2
- package/dist/core/auth/remote-access-store.js +136 -0
- package/dist/core/baseagent-loader.js +4 -4
- package/dist/core/bootstrap-messages.js +39 -18
- package/dist/core/bootstrap-service.js +32 -26
- package/dist/core/capability/capability-manager.js +8 -4
- package/dist/core/capability/providers/codex-capability-provider.js +1 -1
- package/dist/core/command/agent-control.js +7 -2
- package/dist/core/command/command-handler.js +128 -30
- package/dist/core/command/daemon-diagnostics.js +448 -0
- package/dist/core/command/menu-catalog.js +6 -1
- package/dist/core/command/menu-handler.js +410 -98
- package/dist/core/command/menu-protocol.js +123 -0
- package/dist/core/command/slash-gate.js +4 -6
- package/dist/core/command/slash-handler.js +199 -70
- package/dist/core/data-migration.js +5 -15
- package/dist/core/event-catalog.js +3 -0
- package/dist/core/handoff/runtime.js +3 -1
- package/dist/core/handoff/store.js +7 -1
- package/dist/core/message/im-renderer.js +13 -6
- package/dist/core/message/message-bridge.js +5 -36
- package/dist/core/message/message-queue.js +20 -15
- package/dist/core/message/message-utils.js +6 -3
- package/dist/core/message/response-engine.js +147 -94
- package/dist/core/model/catalog-service.js +310 -0
- package/dist/core/model/model-catalog.js +134 -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 +5 -2
- package/dist/core/permission/approval-gateway.js +183 -32
- 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 +257 -31
- package/dist/core/runtime-lock.js +13 -8
- package/dist/core/session/adapters/codex-session-file-adapter.js +71 -3
- package/dist/core/session/session-manager.js +49 -13
- package/dist/core/session/session-renew.js +1 -0
- package/dist/index.js +214 -57
- package/dist/ipc.js +191 -102
- package/dist/paths.js +23 -1
- package/dist/stats/billing.js +18 -4
- package/dist/trigger/manager.js +3 -1
- package/dist/trigger/parser.js +77 -13
- package/dist/trigger/patch.js +104 -1
- package/dist/utils/atomic-write.js +26 -2
- package/dist/utils/cross-platform.js +436 -1
- package/dist/utils/ecweb-supervisor.js +11 -2
- package/dist/utils/ecweb-utils.js +8 -26
- package/dist/utils/error-utils.js +16 -2
- package/dist/utils/model-prices.jsonl +23 -20
- package/dist/utils/npm-ops.js +70 -15
- package/dist/utils/windows-autostart.js +1 -1
- 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/admin.json +2 -0
- package/kits/templates/roles/visitor.json +1 -0
- package/kits/templates/system-fragments/bootstrap.md +8 -1
- package/package.json +16 -4
- package/dist/utils/codex-cli.js +0 -43
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': {
|
|
@@ -722,12 +830,21 @@ export class IpcServer {
|
|
|
722
830
|
|| typeof cmd.toolName !== 'string' || !cmd.toolName) {
|
|
723
831
|
return { ok: false, applicable: true, decision: 'deny', reason: 'invalid Codex hook request' };
|
|
724
832
|
}
|
|
833
|
+
// Unmanaged Codex processes carry no credential and are allowed
|
|
834
|
+
// without consulting any runner; see bin/codex-managed-hook.mjs.
|
|
835
|
+
const sessionToken = typeof cmd.sessionToken === 'string' && cmd.sessionToken
|
|
836
|
+
? cmd.sessionToken
|
|
837
|
+
: undefined;
|
|
838
|
+
if (!sessionToken) {
|
|
839
|
+
return { ok: true, applicable: true, decision: 'allow' };
|
|
840
|
+
}
|
|
725
841
|
try {
|
|
726
842
|
return await this.codexPreToolUseExecutor({
|
|
727
843
|
threadId: cmd.threadId,
|
|
728
844
|
toolName: cmd.toolName,
|
|
729
845
|
toolInput: cmd.toolInput,
|
|
730
846
|
callId: typeof cmd.callId === 'string' ? cmd.callId : undefined,
|
|
847
|
+
sessionToken,
|
|
731
848
|
signal: requestSignal,
|
|
732
849
|
});
|
|
733
850
|
}
|
|
@@ -769,6 +886,19 @@ export class IpcServer {
|
|
|
769
886
|
ttlMs: cmd.ttlMs,
|
|
770
887
|
});
|
|
771
888
|
}
|
|
889
|
+
case 'remote-auth.pair-code': {
|
|
890
|
+
const pair = this.remoteAccessService.getPairingCode(typeof cmd.authorizedBy === 'string' ? cmd.authorizedBy : undefined);
|
|
891
|
+
return { ok: true, ...pair };
|
|
892
|
+
}
|
|
893
|
+
case 'remote-auth.issue-local': {
|
|
894
|
+
return this.remoteAccessService.issueLocalToken(cmd.label);
|
|
895
|
+
}
|
|
896
|
+
case 'remote-auth.redeem': {
|
|
897
|
+
return this.remoteAccessService.redeemPairingCode(cmd.code, cmd.label, cmd.authorizedBy);
|
|
898
|
+
}
|
|
899
|
+
case 'remote-auth.validate': {
|
|
900
|
+
return { ok: true, ...this.remoteAccessService.validateToken(cmd.token) };
|
|
901
|
+
}
|
|
772
902
|
case 'bind.status': {
|
|
773
903
|
if (!this.bindExecutor)
|
|
774
904
|
return { ok: false, error: 'bind executor not configured' };
|
|
@@ -1385,87 +1515,8 @@ export class IpcServer {
|
|
|
1385
1515
|
return { ok: false, error: e?.message ?? String(e) };
|
|
1386
1516
|
}
|
|
1387
1517
|
}
|
|
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
|
-
}
|
|
1518
|
+
case 'monitor-snapshot':
|
|
1519
|
+
return this.getMonitorSnapshot(cmd.leaseId);
|
|
1469
1520
|
case 'monitor-subscribe': {
|
|
1470
1521
|
const leaseId = typeof cmd.leaseId === 'string' ? cmd.leaseId.trim() : '';
|
|
1471
1522
|
if (!leaseId || leaseId.length > 128)
|
|
@@ -1492,12 +1543,26 @@ export class IpcServer {
|
|
|
1492
1543
|
&& crypto.timingSafeEqual(provided, this.controlToken);
|
|
1493
1544
|
}
|
|
1494
1545
|
}
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1546
|
+
export class IpcTransportError extends Error {
|
|
1547
|
+
code;
|
|
1548
|
+
endpoint;
|
|
1549
|
+
constructor(code, endpoint) {
|
|
1550
|
+
const hint = code === 'EPERM' || code === 'EACCES'
|
|
1551
|
+
? '权限被拒绝;Windows 下请检查 daemon 与 CLI 是否使用相同用户和提升权限级别。'
|
|
1552
|
+
: code === 'ETIMEDOUT'
|
|
1553
|
+
? '等待 daemon 响应超时;请求可能已执行,请先检查状态再重试。'
|
|
1554
|
+
: code === 'ENOENT' || code === 'ECONNREFUSED'
|
|
1555
|
+
? 'daemon 未运行或 IPC 端点不存在,请先执行 ec start,再重试。'
|
|
1556
|
+
: '请检查 daemon 进程及 IPC 端点状态。';
|
|
1557
|
+
super(`daemon IPC 失败 (${code}): ${endpoint}。${hint}`);
|
|
1558
|
+
this.code = code;
|
|
1559
|
+
this.endpoint = endpoint;
|
|
1560
|
+
this.name = 'IpcTransportError';
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
/** Query the daemon; opt in to transport errors instead of the legacy null result. */
|
|
1499
1564
|
/** Pass `null` as timeoutMs to wait without a client-side deadline. */
|
|
1500
|
-
export function ipcQuery(socketPath, cmd, timeoutMs = 3000) {
|
|
1565
|
+
export async function ipcQuery(socketPath, cmd, timeoutMs = 3000, options = {}) {
|
|
1501
1566
|
const endpoint = normalizeIpcEndpoint(socketPath);
|
|
1502
1567
|
let socketCandidates = [endpoint];
|
|
1503
1568
|
if (isNamedPipe(endpoint)) {
|
|
@@ -1505,22 +1570,35 @@ export function ipcQuery(socketPath, cmd, timeoutMs = 3000) {
|
|
|
1505
1570
|
if (endpoint === paths.socket)
|
|
1506
1571
|
socketCandidates = getWindowsInstanceSocketPathCandidates(paths.root);
|
|
1507
1572
|
}
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
const
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1573
|
+
const startedAt = Date.now();
|
|
1574
|
+
let failure;
|
|
1575
|
+
for (const candidate of socketCandidates) {
|
|
1576
|
+
const remainingTimeout = timeoutMs === null
|
|
1577
|
+
? null
|
|
1578
|
+
: Math.max(0, timeoutMs - (Date.now() - startedAt));
|
|
1579
|
+
if (remainingTimeout === 0) {
|
|
1580
|
+
failure ??= new IpcTransportError('ETIMEDOUT', candidate);
|
|
1581
|
+
break;
|
|
1582
|
+
}
|
|
1583
|
+
try {
|
|
1518
1584
|
const response = await ipcQueryOnce(candidate, cmd, remainingTimeout);
|
|
1519
1585
|
if (response !== null)
|
|
1520
1586
|
return response;
|
|
1521
1587
|
}
|
|
1522
|
-
|
|
1523
|
-
|
|
1588
|
+
catch (error) {
|
|
1589
|
+
if (!(error instanceof IpcTransportError))
|
|
1590
|
+
throw error;
|
|
1591
|
+
// A missing legacy pipe must not hide a permission failure on the canonical pipe.
|
|
1592
|
+
if (!failure || ['ENOENT', 'ECONNREFUSED'].includes(failure.code))
|
|
1593
|
+
failure = error;
|
|
1594
|
+
// Only connection failures are safe to retry against a legacy endpoint.
|
|
1595
|
+
if (!['ENOENT', 'ECONNREFUSED', 'EPERM', 'EACCES'].includes(error.code))
|
|
1596
|
+
break;
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
if (options.throwOnError && failure)
|
|
1600
|
+
throw failure;
|
|
1601
|
+
return null;
|
|
1524
1602
|
}
|
|
1525
1603
|
function ipcQueryOnce(socketPath, cmd, timeoutMs = 3000) {
|
|
1526
1604
|
let authenticatedCmd = cmd;
|
|
@@ -1545,7 +1623,7 @@ function ipcQueryOnce(socketPath, cmd, timeoutMs = 3000) {
|
|
|
1545
1623
|
? { daemonRuntimeEpoch: process.env[DAEMON_RUNTIME_EPOCH_ENV] }
|
|
1546
1624
|
: {}),
|
|
1547
1625
|
};
|
|
1548
|
-
return new Promise((resolve) => {
|
|
1626
|
+
return new Promise((resolve, reject) => {
|
|
1549
1627
|
const startedAtMono = performance.now();
|
|
1550
1628
|
const queryId = nextIpcQueryId(request);
|
|
1551
1629
|
const commandType = ipcLogToken(request.type);
|
|
@@ -1564,7 +1642,7 @@ function ipcQueryOnce(socketPath, cmd, timeoutMs = 3000) {
|
|
|
1564
1642
|
settled = true;
|
|
1565
1643
|
logPhase('timeout', ` timeout_ms=${timeoutMs}`);
|
|
1566
1644
|
conn.destroy();
|
|
1567
|
-
|
|
1645
|
+
reject(new IpcTransportError('ETIMEDOUT', socketPath));
|
|
1568
1646
|
}, timeoutMs);
|
|
1569
1647
|
conn.on('connect', () => {
|
|
1570
1648
|
if (settled)
|
|
@@ -1590,7 +1668,10 @@ function ipcQueryOnce(socketPath, cmd, timeoutMs = 3000) {
|
|
|
1590
1668
|
parse = 'invalid_json';
|
|
1591
1669
|
}
|
|
1592
1670
|
logPhase('response', ` bytes=${idx + 1} parse=${parse}`);
|
|
1593
|
-
|
|
1671
|
+
if (parse === 'invalid_json')
|
|
1672
|
+
reject(new IpcTransportError('EPROTO', socketPath));
|
|
1673
|
+
else
|
|
1674
|
+
resolve(response);
|
|
1594
1675
|
conn.destroy();
|
|
1595
1676
|
}
|
|
1596
1677
|
});
|
|
@@ -1601,7 +1682,15 @@ function ipcQueryOnce(socketPath, cmd, timeoutMs = 3000) {
|
|
|
1601
1682
|
if (timer)
|
|
1602
1683
|
clearTimeout(timer);
|
|
1603
1684
|
logPhase('error', ` code=${error.code ?? 'unknown'}`);
|
|
1604
|
-
|
|
1685
|
+
reject(new IpcTransportError(error.code ?? 'UNKNOWN', socketPath));
|
|
1686
|
+
});
|
|
1687
|
+
conn.on('close', () => {
|
|
1688
|
+
if (settled)
|
|
1689
|
+
return;
|
|
1690
|
+
settled = true;
|
|
1691
|
+
if (timer)
|
|
1692
|
+
clearTimeout(timer);
|
|
1693
|
+
reject(new IpcTransportError('ECONNRESET', socketPath));
|
|
1605
1694
|
});
|
|
1606
1695
|
});
|
|
1607
1696
|
}
|
package/dist/paths.js
CHANGED
|
@@ -129,11 +129,22 @@ export function agentHandoffDir(aid) {
|
|
|
129
129
|
return path.join(agentDataDir(aid), 'handoff');
|
|
130
130
|
}
|
|
131
131
|
export function agentOutboxPath(aid) {
|
|
132
|
-
return path.join(
|
|
132
|
+
return path.join(resolvePaths().outboxDir, encodeURIComponent(aid), 'outbox.jsonl');
|
|
133
133
|
}
|
|
134
134
|
export function agentActivityOutboxPath(aid) {
|
|
135
|
+
return path.join(resolvePaths().outboxDir, encodeURIComponent(aid), 'activity-outbox.jsonl');
|
|
136
|
+
}
|
|
137
|
+
/** Pre-v4 per-Agent outbox paths retained only for compatibility import. */
|
|
138
|
+
export function legacyAgentOutboxPath(aid) {
|
|
139
|
+
return path.join(agentDataDir(aid), 'outbox.jsonl');
|
|
140
|
+
}
|
|
141
|
+
export function legacyAgentActivityOutboxPath(aid) {
|
|
135
142
|
return path.join(agentDataDir(aid), 'activity-outbox.jsonl');
|
|
136
143
|
}
|
|
144
|
+
/** Earliest global outbox layout: data/outbox/<aid>.jsonl. */
|
|
145
|
+
export function legacyFlatOutboxPath(aid) {
|
|
146
|
+
return path.join(resolvePaths().outboxDir, `${aid}.jsonl`);
|
|
147
|
+
}
|
|
137
148
|
/** Directory-safe channel instance key used by connector runtime state. */
|
|
138
149
|
function encodePathKey(value) {
|
|
139
150
|
return encodeURIComponent(value);
|
|
@@ -283,6 +294,17 @@ function migrateFromAun() {
|
|
|
283
294
|
const markerPath = path.join(newRoot, MIGRATION_MARKER);
|
|
284
295
|
if (fs.existsSync(markerPath))
|
|
285
296
|
return;
|
|
297
|
+
// Test workers use isolated temporary homes. Copying the developer's
|
|
298
|
+
// machine-wide ~/.aun tree into every fresh fixture is both unnecessary and
|
|
299
|
+
// can make process-boundary tests exceed their timeout. Production roots
|
|
300
|
+
// retain the normal one-time migration behavior.
|
|
301
|
+
if (process.env.EVOLCORE_TEST === '1') {
|
|
302
|
+
try {
|
|
303
|
+
fs.writeFileSync(markerPath, new Date().toISOString(), 'utf-8');
|
|
304
|
+
}
|
|
305
|
+
catch { }
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
286
308
|
const oldRoot = path.join(os.homedir(), '.aun');
|
|
287
309
|
if (!fs.existsSync(oldRoot) || oldRoot === newRoot) {
|
|
288
310
|
try {
|
package/dist/stats/billing.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* billing.ts — 读 model-prices.jsonl
|
|
2
|
+
* billing.ts — 读 model-prices.jsonl,按模型参数中的 billing_fn 计算费用。
|
|
3
3
|
* 费用查询时实时计算,不存入 DB(价格可能变动)。
|
|
4
4
|
*/
|
|
5
5
|
import fs from 'fs';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import { getPackageRoot } from '../paths.js';
|
|
8
|
-
//
|
|
8
|
+
// 模型参数缓存(进程内,5 分钟 TTL)
|
|
9
|
+
let _parameterCache = null;
|
|
10
|
+
let _parameterCacheTs = 0;
|
|
9
11
|
let _priceCache = null;
|
|
10
12
|
let _priceCacheTs = 0;
|
|
11
13
|
const PRICE_CACHE_TTL = 5 * 60 * 1000;
|
|
@@ -44,6 +46,14 @@ function _loadJsonlMerged(evolcoreHome, filename) {
|
|
|
44
46
|
}
|
|
45
47
|
return results;
|
|
46
48
|
}
|
|
49
|
+
function loadModelParameters(evolcoreHome) {
|
|
50
|
+
if (_parameterCache && Date.now() - _parameterCacheTs < PRICE_CACHE_TTL)
|
|
51
|
+
return _parameterCache;
|
|
52
|
+
const params = _loadJsonlMerged(evolcoreHome, 'model-prices.jsonl');
|
|
53
|
+
_parameterCache = params;
|
|
54
|
+
_parameterCacheTs = Date.now();
|
|
55
|
+
return params;
|
|
56
|
+
}
|
|
47
57
|
let _aliasCache = null;
|
|
48
58
|
let _aliasCacheTs = 0;
|
|
49
59
|
function loadAliases(evolcoreHome) {
|
|
@@ -64,7 +74,8 @@ function loadPrices(evolcoreHome) {
|
|
|
64
74
|
const now = Date.now();
|
|
65
75
|
if (_priceCache && now - _priceCacheTs < PRICE_CACHE_TTL)
|
|
66
76
|
return _priceCache;
|
|
67
|
-
_priceCache =
|
|
77
|
+
_priceCache = loadModelParameters(evolcoreHome).filter((record) => (typeof record.billing_fn === 'string'
|
|
78
|
+
&& (record.currency === 'USD' || record.currency === 'CNY')));
|
|
68
79
|
_priceCacheTs = now;
|
|
69
80
|
return _priceCache;
|
|
70
81
|
}
|
|
@@ -140,13 +151,16 @@ export function calcCost(evolcoreHome, event) {
|
|
|
140
151
|
return {};
|
|
141
152
|
return fn(event, priceRow);
|
|
142
153
|
}
|
|
154
|
+
// ── Model Specs ──────────────────────────────────────────────────────────────
|
|
143
155
|
let _specCache = null;
|
|
144
156
|
let _specCacheTs = 0;
|
|
145
157
|
function loadSpecs(evolcoreHome) {
|
|
146
158
|
const now = Date.now();
|
|
147
159
|
if (_specCache && now - _specCacheTs < PRICE_CACHE_TTL)
|
|
148
160
|
return _specCache;
|
|
149
|
-
_specCache =
|
|
161
|
+
_specCache = loadModelParameters(evolcoreHome).filter((record) => (typeof record.context_window === 'number'
|
|
162
|
+
&& typeof record.max_input_tokens === 'number'
|
|
163
|
+
&& typeof record.max_output_tokens === 'number'));
|
|
150
164
|
_specCacheTs = now;
|
|
151
165
|
return _specCache;
|
|
152
166
|
}
|
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 };
|