evolcore 0.0.33 → 0.0.35-test.0
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 +59 -0
- package/bin/ec.js +3 -2
- package/bin/install-codex-managed-hooks.mjs +38 -4
- package/dist/agents/baseagent-availability.js +20 -36
- package/dist/agents/claude-runner.js +93 -14
- package/dist/agents/codex/diagnostics.js +79 -22
- package/dist/agents/codex-runner.js +245 -47
- package/dist/agents/ecagent-runner.js +90 -27
- package/dist/agents/runner-types.js +5 -6
- package/dist/aun/aid/agentmd.js +13 -2
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/identity.js +1 -1
- package/dist/aun/msg/managed-operation.js +16 -0
- package/dist/aun/msg/p2p.js +18 -2
- package/dist/channels/aun.js +123 -7
- package/dist/channels/daemon.js +6 -3
- package/dist/cli/aun-commands.js +39 -14
- package/dist/cli/ctl-command.js +3 -1
- package/dist/cli/daemon-commands.js +130 -23
- package/dist/cli/fs-command.js +2 -1
- package/dist/cli/getmeta-command.js +211 -0
- package/dist/cli/index.js +26 -6
- package/dist/cli/integrity-command.js +196 -0
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/memory-command.js +200 -156
- package/dist/cli/thread-command.js +17 -7
- package/dist/cli/trigger-command.js +6 -1
- package/dist/config/config-field-policy.js +4 -0
- package/dist/config/config-manager.js +19 -0
- package/dist/config/contact-book-store.js +5 -0
- package/dist/config/resolved-config-op.js +25 -8
- package/dist/config/role-service.js +13 -0
- package/dist/config/runtime-env.js +16 -0
- package/dist/config-store.js +30 -0
- package/dist/core/audit/log-integrity.js +3 -1
- package/dist/core/auth/agent-delegation.js +108 -35
- package/dist/core/auth/delegation-diagnostics.js +414 -0
- package/dist/core/auth/operation-authorizer.js +22 -3
- package/dist/core/auth/operation-catalog.js +16 -0
- package/dist/core/channel-runtime.js +1 -1
- package/dist/core/command/agent-control.js +18 -26
- package/dist/core/command/command-handler.js +145 -5
- package/dist/core/command/daemon-diagnostics.js +1 -0
- package/dist/core/command/menu-catalog.js +3 -0
- package/dist/core/command/menu-handler.js +24 -7
- package/dist/core/command/menu-protocol.js +1 -0
- package/dist/core/command/slash-handler.js +2 -0
- package/dist/core/command/topic-slash.js +4 -7
- package/dist/core/daemon-bootstrap.js +4 -7
- package/dist/core/event-catalog.js +14 -0
- package/dist/core/file-integrity/read.js +61 -0
- package/dist/core/file-integrity/store.js +262 -0
- package/dist/core/ipc-registration.js +47 -2
- package/dist/core/memory/batch-meta.js +133 -0
- package/dist/core/memory/consolidation-context.js +132 -28
- package/dist/core/memory/consolidation-ops-records.js +14 -16
- package/dist/core/memory/consolidation-trigger.js +15 -7
- package/dist/core/memory/consolidation.js +146 -26
- package/dist/core/memory/index-manager.js +19 -28
- package/dist/core/memory/memory-admin.js +19 -14
- package/dist/core/memory/memory-document.js +53 -9
- package/dist/core/memory/memory-writer.js +50 -19
- package/dist/core/message/aun-ipc-sender.js +19 -1
- package/dist/core/message/im-renderer.js +20 -4
- package/dist/core/message/message-bridge.js +5 -4
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/peer-mode.js +17 -0
- package/dist/core/message/response-engine.js +598 -50
- package/dist/core/permission/ec-command-parser.js +169 -44
- package/dist/core/permission/protected-paths.js +5 -1
- package/dist/core/permission/readonly-shell-query.js +43 -5
- package/dist/core/permission/session-path-grants.js +334 -0
- package/dist/core/permission/tool-canonical.js +226 -0
- package/dist/core/permission/tool-policy.js +60 -62
- package/dist/core/relation/peer-identity.js +87 -12
- package/dist/core/role/runtime-policy.js +21 -10
- package/dist/core/session/session-fs-store.js +123 -8
- package/dist/core/session/session-manager.js +131 -6
- package/dist/core/session/session-title.js +26 -1
- package/dist/core/skill/index-generator.js +17 -3
- package/dist/core/system-session/grants.js +40 -0
- package/dist/core/system-session/memory.js +289 -0
- package/dist/core/system-session/registry.js +74 -0
- package/dist/core/system-session/types.js +14 -0
- package/dist/core/thread/command-parser.js +6 -1
- package/dist/core/thread/ipc-operation.js +33 -16
- package/dist/core/thread/thread-operation.js +62 -24
- package/dist/core/thread/thread-service.js +258 -51
- package/dist/eck/kit-renderer.js +7 -2
- package/dist/eck/manifest-engine.js +24 -4
- package/dist/index.js +11 -2
- package/dist/ipc.js +51 -40
- package/dist/paths.js +70 -0
- package/dist/stats/billing.js +20 -8
- package/dist/trigger/feedback.js +6 -1
- package/dist/trigger/parser.js +8 -1
- package/dist/trigger/system-definitions.js +3 -3
- package/dist/utils/cross-platform.js +17 -5
- package/dist/utils/error-dict.json +18 -4
- package/dist/utils/error-utils.js +121 -0
- package/dist/utils/instance-registry.js +13 -8
- package/dist/utils/log-writer.js +12 -0
- package/dist/utils/process-introspect.js +11 -0
- package/kits/docs/context-assembly.md +0 -1
- package/kits/docs/evolcore/agent-create.md +5 -4
- package/kits/docs/evolcore/agent.md +2 -2
- package/kits/docs/evolcore/config.md +2 -2
- package/kits/docs/evolcore/ctl.md +2 -2
- package/kits/docs/evolcore/ec-capabilities.md +1 -1
- package/kits/docs/evolcore/fs-architecture.md +9 -10
- package/kits/docs/evolcore/fs.md +2 -2
- package/kits/docs/evolcore/group-collaboration.md +3 -3
- package/kits/docs/evolcore/group.md +14 -12
- package/kits/docs/evolcore/memory-architecture.md +4 -5
- package/kits/docs/evolcore/memory-consolidation.md +19 -16
- package/kits/docs/evolcore/memory-tags.md +3 -1
- package/kits/docs/evolcore/memory.md +24 -12
- package/kits/docs/evolcore/model.md +16 -0
- package/kits/docs/evolcore/msg.md +7 -7
- package/kits/docs/evolcore/stats.md +1 -1
- package/kits/docs/evolcore/thread-chain.md +30 -14
- package/kits/docs/prompt-loading-architecture.md +0 -1
- package/kits/eck_manifest.auxiliary.json +9 -0
- package/kits/eck_manifest.json +20 -20
- package/kits/eck_manifest.memory.json +20 -12
- package/kits/rules/01-overview.md +11 -0
- package/kits/rules/03-identity.md +6 -3
- package/kits/rules/06-channel.md +1 -1
- package/kits/rules/07-memory-and-thread.md +13 -16
- package/kits/schemas/_meta.json +7 -1
- package/kits/schemas/agent-config.schema.13.json +1 -1
- package/kits/schemas/daemon.schema.7.json +140 -0
- package/kits/skills/ec-group/SKILL.md +8 -5
- package/kits/skills/ec-skill/scripts/cli.mjs +1 -1
- package/kits/skills/ec-skill/scripts/skill.mjs +1 -1
- package/kits/skills/thread-ops/SKILL.md +18 -18
- package/kits/templates/memory-consolidation/forget.md +2 -2
- package/kits/templates/memory-consolidation/generalize.md +4 -4
- package/kits/templates/memory-consolidation/notes.md +12 -12
- package/kits/templates/memory-consolidation/profile.md +3 -3
- package/kits/templates/memory-consolidation/promote.md +3 -3
- package/kits/templates/memory-consolidation/workflow.md +42 -7
- package/kits/templates/message-fragments/handoff-request-to-target.md +1 -1
- package/kits/templates/system-fragments/bash-description.md +9 -0
- package/kits/templates/system-fragments/bootstrap.md +4 -3
- package/kits/templates/system-fragments/channel.md +2 -2
- package/kits/templates/system-fragments/memory.md +16 -9
- package/kits/templates/system-fragments/reply.md +6 -6
- package/kits/templates/system-fragments/session.md +1 -1
- package/package.json +4 -3
- package/dist/core/memory/memory-session-policy.js +0 -41
- package/dist/core/memory/memory-signature.js +0 -257
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { resolvePaths, agentThreadIndexPath } from '../paths.js';
|
|
4
|
+
import { scanChatDirs, readLastJsonlLine, } from '../core/session/session-fs-store.js';
|
|
5
|
+
const HELP_TEXT = `
|
|
6
|
+
ec getmeta <id> [选项]
|
|
7
|
+
|
|
8
|
+
按任意一种会话标识查询本地会话的完整元数据(不经过 daemon,直接读存档)。
|
|
9
|
+
|
|
10
|
+
<id> 接受三种形态:
|
|
11
|
+
meta_YYYYMMDD_<ts> ec 会话 id(meta 文件名)
|
|
12
|
+
thread-<...> 话题 id(threadId)
|
|
13
|
+
<uuid> provider 侧会话 id(agentSessionId)
|
|
14
|
+
|
|
15
|
+
输出字段:
|
|
16
|
+
sessionId / threadId / agentSessionId / peer / selfAID / baseagent
|
|
17
|
+
name / chatType / projectPath / updatedAt / metaFile
|
|
18
|
+
source 命中位置:meta(主会话)/ thread(话题)/ active / trash
|
|
19
|
+
indexed 该会话是否登记在话题索引里
|
|
20
|
+
|
|
21
|
+
选项:
|
|
22
|
+
--self <aid> 只扫描指定 agent,减少全量扫描开销
|
|
23
|
+
--format json 以 JSON 格式输出
|
|
24
|
+
`.trim();
|
|
25
|
+
function argValue(args, flag) {
|
|
26
|
+
const i = args.indexOf(flag);
|
|
27
|
+
return i >= 0 ? args[i + 1] : undefined;
|
|
28
|
+
}
|
|
29
|
+
function hasFlag(args, flag) {
|
|
30
|
+
return args.includes(flag);
|
|
31
|
+
}
|
|
32
|
+
function readJsonFile(filePath) {
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** 该 agent 的话题索引里是否登记了 (peer, threadId, sessionId)。 */
|
|
41
|
+
function isIndexed(selfAID, peer, threadId, sessionId) {
|
|
42
|
+
if (!selfAID)
|
|
43
|
+
return false;
|
|
44
|
+
const raw = readJsonFile(agentThreadIndexPath(selfAID));
|
|
45
|
+
if (!raw?.threads)
|
|
46
|
+
return false;
|
|
47
|
+
return raw.threads.some(t => (t.sessionId === sessionId
|
|
48
|
+
&& (t.threadId ?? '') === threadId
|
|
49
|
+
&& (peer == null || t.peer === peer)));
|
|
50
|
+
}
|
|
51
|
+
function makeHit(meta, metaFile, source, selfAID, chatPeer) {
|
|
52
|
+
const threadId = meta.threadId ?? '';
|
|
53
|
+
return {
|
|
54
|
+
sessionId: meta.id,
|
|
55
|
+
threadId,
|
|
56
|
+
agentSessionId: meta.agentSessionId ?? null,
|
|
57
|
+
peer: meta.channelId ?? chatPeer ?? null,
|
|
58
|
+
selfAID: meta.selfAID ?? selfAID,
|
|
59
|
+
baseagent: meta.baseagent ?? null,
|
|
60
|
+
name: meta.name ?? null,
|
|
61
|
+
chatType: meta.chatType ?? null,
|
|
62
|
+
projectPath: meta.projectPath ?? null,
|
|
63
|
+
updatedAt: meta.updatedAt ?? null,
|
|
64
|
+
updatedAtStr: meta.updatedAtStr ?? null,
|
|
65
|
+
createdAtStr: meta.createdAtStr ?? null,
|
|
66
|
+
metaFile,
|
|
67
|
+
source,
|
|
68
|
+
indexed: isIndexed(meta.selfAID ?? selfAID, meta.channelId ?? chatPeer ?? null, threadId, meta.id),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* id 形态判定。
|
|
73
|
+
* - `meta_...` 按文件名直接命中,不读文件内容
|
|
74
|
+
* - `thread-...` / uuid 需要扫末行比对,成本高但只在必要时发生
|
|
75
|
+
*/
|
|
76
|
+
function classifyId(id) {
|
|
77
|
+
if (id.startsWith('meta_'))
|
|
78
|
+
return 'session';
|
|
79
|
+
if (id.startsWith('thread-'))
|
|
80
|
+
return 'thread';
|
|
81
|
+
return 'agentSession';
|
|
82
|
+
}
|
|
83
|
+
async function cmdGetMeta(args) {
|
|
84
|
+
const jsonFormat = hasFlag(args, '--format') && argValue(args, '--format') === 'json'
|
|
85
|
+
|| hasFlag(args, '--json');
|
|
86
|
+
// 位置参数 = 第一个不是 flag、也不属于任何 flag 取值的参数。
|
|
87
|
+
// 不能简单取"首个非 - 开头",否则 `--self <aid>` 的值会被当成 id。
|
|
88
|
+
const VALUE_FLAGS = new Set(['--self', '--format']);
|
|
89
|
+
let rawId;
|
|
90
|
+
for (let i = 0; i < args.length; i++) {
|
|
91
|
+
const a = args[i];
|
|
92
|
+
if (a.startsWith('-')) {
|
|
93
|
+
if (VALUE_FLAGS.has(a))
|
|
94
|
+
i++;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
rawId = a;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
if (!rawId) {
|
|
101
|
+
console.error('用法: ec getmeta <sessionId|threadId|agentSessionId> [--self <aid>] [--format json]');
|
|
102
|
+
process.exitCode = 1;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const id = rawId.endsWith('.jsonl') ? rawId.slice(0, -'.jsonl'.length) : rawId;
|
|
106
|
+
const selfFilter = argValue(args, '--self');
|
|
107
|
+
const kind = classifyId(id);
|
|
108
|
+
const p = resolvePaths();
|
|
109
|
+
const chatDirs = scanChatDirs(p.sessionsDir).filter(c => !selfFilter || c.selfAID === selfFilter);
|
|
110
|
+
const hits = [];
|
|
111
|
+
for (const chat of chatDirs) {
|
|
112
|
+
// 主会话 meta 在目录根,话题在 _threads/,已删除话题在 _trash/
|
|
113
|
+
const locations = [
|
|
114
|
+
{ dir: chat.dirPath, source: 'meta' },
|
|
115
|
+
{ dir: path.join(chat.dirPath, '_threads'), source: 'thread' },
|
|
116
|
+
{ dir: path.join(chat.dirPath, '_trash'), source: 'trash' },
|
|
117
|
+
];
|
|
118
|
+
for (const loc of locations) {
|
|
119
|
+
let entries;
|
|
120
|
+
try {
|
|
121
|
+
entries = fs.readdirSync(loc.dir);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
for (const entry of entries) {
|
|
127
|
+
if (!entry.endsWith('.jsonl') || entry.includes('.fork-history'))
|
|
128
|
+
continue;
|
|
129
|
+
const entryId = entry.slice(0, -'.jsonl'.length);
|
|
130
|
+
// 文件名直接命中时只读这一个文件;否则必须读末行比对
|
|
131
|
+
if (kind === 'session' && entryId !== id)
|
|
132
|
+
continue;
|
|
133
|
+
const filePath = path.join(loc.dir, entry);
|
|
134
|
+
const meta = readLastJsonlLine(filePath);
|
|
135
|
+
if (!meta)
|
|
136
|
+
continue;
|
|
137
|
+
if (kind === 'thread' && (meta.threadId ?? '') !== id)
|
|
138
|
+
continue;
|
|
139
|
+
if (kind === 'agentSession' && (meta.agentSessionId ?? '') !== id)
|
|
140
|
+
continue;
|
|
141
|
+
if (kind === 'session' && meta.id !== id && entryId !== id)
|
|
142
|
+
continue;
|
|
143
|
+
hits.push(makeHit(meta, filePath, loc.source, chat.selfAID, chat.channelId));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// 兜底:active.json 的 id/threadId/agentSessionId 也可能命中(active 是 meta 的最新快照)
|
|
148
|
+
if (hits.length === 0) {
|
|
149
|
+
for (const chat of chatDirs) {
|
|
150
|
+
const active = readJsonFile(path.join(chat.dirPath, 'active.json'));
|
|
151
|
+
if (!active)
|
|
152
|
+
continue;
|
|
153
|
+
const match = kind === 'session'
|
|
154
|
+
? active.id === id
|
|
155
|
+
: kind === 'thread'
|
|
156
|
+
? (active.threadId ?? '') === id
|
|
157
|
+
: (active.agentSessionId ?? '') === id;
|
|
158
|
+
if (!match)
|
|
159
|
+
continue;
|
|
160
|
+
hits.push(makeHit(active, path.join(chat.dirPath, 'active.json'), 'active', chat.selfAID, chat.channelId));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (hits.length === 0) {
|
|
164
|
+
if (jsonFormat) {
|
|
165
|
+
console.log(JSON.stringify({ ok: false, id, kind, hits: [] }, null, 2));
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
console.error(`✗ 未找到会话 ${id}(${kind === 'session' ? 'ec 会话 id' : kind === 'thread' ? '话题 id' : 'provider 会话 id'};已扫描 ${chatDirs.length} 个 chat 目录)`);
|
|
169
|
+
}
|
|
170
|
+
process.exitCode = 1;
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (jsonFormat) {
|
|
174
|
+
console.log(JSON.stringify({ ok: true, id, kind, hits }, null, 2));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const sourceLabel = {
|
|
178
|
+
meta: '主会话 meta 文件',
|
|
179
|
+
thread: '话题 meta 文件',
|
|
180
|
+
active: 'active.json 快照',
|
|
181
|
+
trash: '已删除(_trash)',
|
|
182
|
+
};
|
|
183
|
+
for (const h of hits) {
|
|
184
|
+
console.log(`会话 id: ${h.sessionId}`);
|
|
185
|
+
console.log(` 话题 id: ${h.threadId || '(主会话)'}`);
|
|
186
|
+
console.log(` provider 会话: ${h.agentSessionId ?? '(空)'}`);
|
|
187
|
+
console.log(` 来源: ${sourceLabel[h.source]}${h.source === 'trash' ? ' ⚠ 话题已删除' : ''}`);
|
|
188
|
+
console.log(` 索引: ${h.indexed ? '已登记' : '未登记(仅磁盘存档)'}`);
|
|
189
|
+
console.log(` 名称: ${h.name ?? '(未命名)'}`);
|
|
190
|
+
if (h.chatType)
|
|
191
|
+
console.log(` 类型: ${h.chatType === 'group' ? '群聊' : '单聊'}`);
|
|
192
|
+
console.log(` 对端: ${h.peer ?? ''}`);
|
|
193
|
+
console.log(` 本端 AID: ${h.selfAID ?? ''}`);
|
|
194
|
+
if (h.baseagent)
|
|
195
|
+
console.log(` 基座: ${h.baseagent}`);
|
|
196
|
+
if (h.projectPath)
|
|
197
|
+
console.log(` 项目: ${h.projectPath}`);
|
|
198
|
+
if (h.updatedAtStr)
|
|
199
|
+
console.log(` 最近更新: ${h.updatedAtStr}`);
|
|
200
|
+
console.log(` 文件: ${h.metaFile}`);
|
|
201
|
+
console.log('');
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
export async function runGetMetaCommand(args) {
|
|
205
|
+
const sub = args[0];
|
|
206
|
+
if (!sub || sub === 'help' || hasFlag(args, '--help') || hasFlag(args, '-h')) {
|
|
207
|
+
console.log(HELP_TEXT);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
await cmdGetMeta(args);
|
|
211
|
+
}
|
package/dist/cli/index.js
CHANGED
|
@@ -37,6 +37,7 @@ Agent 与会话:
|
|
|
37
37
|
ctl 当前托管会话的控制命令
|
|
38
38
|
queue 查询或操作 agent 消息队列
|
|
39
39
|
session 按 ec 会话 id 查询本地 session 存档(含 agentSessionId)
|
|
40
|
+
getmeta 按会话 id / threadId / agentSessionId 查询会话元数据
|
|
40
41
|
task 恢复暂停或异常中断的会话任务
|
|
41
42
|
trigger 管理定时与事件触发器
|
|
42
43
|
handoff 查询和完成跨会话回流
|
|
@@ -78,7 +79,7 @@ AUN:
|
|
|
78
79
|
EVENT_LOG 事件日志(默认: true)`;
|
|
79
80
|
function printLifecycleHelp(command) {
|
|
80
81
|
const help = {
|
|
81
|
-
start: `用法: ec start [--foreground] [--diagnose|-d]\n\n --foreground 前台运行,适合 systemd 或容器直接管理\n --diagnose, -d 以诊断模式启动 daemon
|
|
82
|
+
start: `用法: ec start [--foreground] [--diagnose|-d] [--debug] [--as-mode dev|install]\n\n --foreground 前台运行,适合 systemd 或容器直接管理\n --diagnose, -d 以诊断模式启动 daemon\n --debug 调试模式:注册调试 IPC,可在宿主终端以任意 session 身份发命令\n --as-mode 覆盖运行模式判定(仅本次启动生效,不写配置):\n install = 按安装模式(校验随包文件完整性,篡改自动恢复)\n dev = 按开发模式(不校验随包文件)`,
|
|
82
83
|
stop: '用法: ec stop\n\n停止当前 EVOLCORE_HOME 下运行的 EvolCore 服务。',
|
|
83
84
|
restart: `用法: ec restart [--clear] [--diagnose|-d]\n\n --clear 同时清理跨 HOME 残留的 EvolCore 主进程\n --diagnose, -d 以诊断模式启动新 daemon`,
|
|
84
85
|
status: '用法: ec status\n\n查看 daemon、agent、渠道与会话运行状态。',
|
|
@@ -237,11 +238,20 @@ export async function main(args) {
|
|
|
237
238
|
printLifecycleHelp('start');
|
|
238
239
|
break;
|
|
239
240
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
241
|
+
{
|
|
242
|
+
const asModeRaw = getArgValue(strippedArgs, '--as-mode');
|
|
243
|
+
if (asModeRaw !== undefined && asModeRaw !== 'dev' && asModeRaw !== 'install') {
|
|
244
|
+
console.error(`❌ --as-mode 只接受 dev 或 install,收到: ${asModeRaw}`);
|
|
245
|
+
process.exitCode = 1;
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
await cmdStart({
|
|
249
|
+
diagnose: strippedArgs.includes('--diagnose') || strippedArgs.includes('-d'),
|
|
250
|
+
foreground: strippedArgs.includes('--foreground'),
|
|
251
|
+
debug: strippedArgs.includes('--debug'),
|
|
252
|
+
asMode: asModeRaw,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
245
255
|
break;
|
|
246
256
|
case 'stop':
|
|
247
257
|
if (wantsHelp(strippedArgs.slice(1))) {
|
|
@@ -339,6 +349,11 @@ export async function main(args) {
|
|
|
339
349
|
await cmdNet(strippedArgs.slice(1));
|
|
340
350
|
break;
|
|
341
351
|
}
|
|
352
|
+
case 'integrity': {
|
|
353
|
+
const { cmdIntegrity } = await import('./integrity-command.js');
|
|
354
|
+
await cmdIntegrity(strippedArgs.slice(1));
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
342
357
|
case 'ctl':
|
|
343
358
|
await cmdCtl(strippedArgs.slice(1));
|
|
344
359
|
break;
|
|
@@ -435,6 +450,11 @@ export async function main(args) {
|
|
|
435
450
|
await runSessionCommand(strippedArgs.slice(1));
|
|
436
451
|
break;
|
|
437
452
|
}
|
|
453
|
+
case 'getmeta': {
|
|
454
|
+
const { runGetMetaCommand } = await import('./getmeta-command.js');
|
|
455
|
+
await runGetMetaCommand(strippedArgs.slice(1));
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
438
458
|
case 'group': {
|
|
439
459
|
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
440
460
|
suppressSdkLogs();
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* integrity-command.ts — `ec integrity` 子命令
|
|
3
|
+
*
|
|
4
|
+
* 子命令:
|
|
5
|
+
* ec integrity check <path> — 检查单个文件的台账状态(不修改,不恢复)
|
|
6
|
+
* ec integrity seal <path> — 把当前磁盘内容作为新基线登记(确认一次合法的带外修改)
|
|
7
|
+
* ec integrity query <path> — 打印台账记录(sha256、时间、登记方)
|
|
8
|
+
* ec integrity list — 列出所有已登记文件的状态摘要
|
|
9
|
+
*
|
|
10
|
+
* 所有操作都是本地 CLI,不经过 daemon IPC。
|
|
11
|
+
* 注意:ec integrity 不在 managed 白名单里,agent 会话内执行会被拒绝。
|
|
12
|
+
*/
|
|
13
|
+
import { createRequire } from 'module';
|
|
14
|
+
import { checkFile, queryRecord, registerFile } from '../core/file-integrity/store.js';
|
|
15
|
+
import { integrityDbPath, resolveRoot } from '../paths.js';
|
|
16
|
+
import fs from 'fs';
|
|
17
|
+
import path from 'path';
|
|
18
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
19
|
+
const HELP = `用法: ec integrity <subcommand> [path]
|
|
20
|
+
|
|
21
|
+
子命令:
|
|
22
|
+
check <path> 检查文件台账状态(不修改,不恢复文件)
|
|
23
|
+
seal <path> 把当前磁盘内容登记为新基线(确认合法的带外修改)
|
|
24
|
+
query <path> 查看台账记录(hash、时间、登记方)
|
|
25
|
+
list 列出所有已登记文件的摘要状态
|
|
26
|
+
|
|
27
|
+
说明:
|
|
28
|
+
台账库位于 $EVOLCORE_HOME/data/integrity/file-integrity.db
|
|
29
|
+
"seal" 操作相当于手动确认一次合法修改,使其成为新的校验基线。
|
|
30
|
+
在开发模式下(非 node_modules 安装),系统文件不会自动校验,
|
|
31
|
+
但可以用 seal 主动建立基线,便于后续对比。
|
|
32
|
+
`;
|
|
33
|
+
function fmtTime(unix) {
|
|
34
|
+
if (!unix)
|
|
35
|
+
return '—';
|
|
36
|
+
return new Date(unix * 1000).toLocaleString('zh-CN', {
|
|
37
|
+
year: 'numeric', month: '2-digit', day: '2-digit',
|
|
38
|
+
hour: '2-digit', minute: '2-digit', second: '2-digit',
|
|
39
|
+
hour12: false,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function fmtHash(h) {
|
|
43
|
+
return h.slice(0, 16) + '…';
|
|
44
|
+
}
|
|
45
|
+
function resolveTarget(arg) {
|
|
46
|
+
return path.isAbsolute(arg) ? arg : path.resolve(process.cwd(), arg);
|
|
47
|
+
}
|
|
48
|
+
function cmdCheck(target) {
|
|
49
|
+
const result = checkFile(target);
|
|
50
|
+
const rel = path.relative(resolveRoot(), target) || path.basename(target);
|
|
51
|
+
switch (result.status) {
|
|
52
|
+
case 'missing':
|
|
53
|
+
console.log(`❓ 文件不存在: ${rel}`);
|
|
54
|
+
process.exitCode = 1;
|
|
55
|
+
break;
|
|
56
|
+
case 'unregistered':
|
|
57
|
+
console.log(`🆕 未登记(使用 seal 建立基线): ${rel}`);
|
|
58
|
+
process.exitCode = 1;
|
|
59
|
+
break;
|
|
60
|
+
case 'verified':
|
|
61
|
+
console.log(`✅ 校验通过: ${rel}`);
|
|
62
|
+
console.log(` sha256: ${fmtHash(result.actual)}`);
|
|
63
|
+
break;
|
|
64
|
+
case 'tampered':
|
|
65
|
+
console.log(`❌ 已被修改: ${rel}`);
|
|
66
|
+
console.log(` 预期: ${fmtHash(result.expected)}`);
|
|
67
|
+
console.log(` 实际: ${fmtHash(result.actual)}`);
|
|
68
|
+
console.log(` 可恢复: ${result.restorable ? '是(快照完好)' : '否(快照已损坏)'}`);
|
|
69
|
+
process.exitCode = 1;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function cmdSeal(target) {
|
|
74
|
+
if (!fs.existsSync(target)) {
|
|
75
|
+
console.error(`❌ 文件不存在: ${target}`);
|
|
76
|
+
process.exitCode = 1;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
registerFile(target);
|
|
80
|
+
const rel = path.relative(resolveRoot(), target) || path.basename(target);
|
|
81
|
+
console.log(`✅ 已登记为新基线: ${rel}`);
|
|
82
|
+
}
|
|
83
|
+
function cmdQuery(target) {
|
|
84
|
+
const rec = queryRecord(target);
|
|
85
|
+
const rel = path.relative(resolveRoot(), target) || path.basename(target);
|
|
86
|
+
if (!rec) {
|
|
87
|
+
console.log(`🆕 台账无记录: ${rel}`);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
console.log(`文件: ${rel}`);
|
|
91
|
+
console.log(` sha256: ${rec.sha256}`);
|
|
92
|
+
console.log(` 登记时间: ${fmtTime(rec.registered_at)}`);
|
|
93
|
+
console.log(` 最后校验: ${fmtTime(rec.last_verified_at)}`);
|
|
94
|
+
console.log(` 登记方: ${rec.registrar_aid ?? '(系统)'}`);
|
|
95
|
+
}
|
|
96
|
+
function cmdList() {
|
|
97
|
+
if (!fs.existsSync(integrityDbPath())) {
|
|
98
|
+
console.log('台账库尚未创建(还没有任何文件被登记过)');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
let DatabaseSync;
|
|
102
|
+
try {
|
|
103
|
+
({ DatabaseSync } = requireFromHere('node:sqlite'));
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
console.error('❌ node:sqlite 不可用(需要 Node.js 22.5+)');
|
|
107
|
+
process.exitCode = 1;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const db = new DatabaseSync(integrityDbPath(), { readonly: true });
|
|
111
|
+
const rows = db.prepare('SELECT file_path, sha256, registered_at, last_verified_at FROM file_integrity ORDER BY file_path').all();
|
|
112
|
+
if (rows.length === 0) {
|
|
113
|
+
console.log('台账为空');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// 台账主键在 Windows 下是小写,root 也要按同样规则归一化才能前缀匹配
|
|
117
|
+
const rootSlash = resolveRoot().replace(/\\/g, '/');
|
|
118
|
+
const root = (process.platform === 'win32' ? rootSlash.toLowerCase() : rootSlash) + '/';
|
|
119
|
+
let ok = 0, tampered = 0, missing = 0;
|
|
120
|
+
console.log(`台账共 ${rows.length} 条记录(${integrityDbPath()})\n`);
|
|
121
|
+
for (const row of rows) {
|
|
122
|
+
const rel = row.file_path.replace(/\\/g, '/').startsWith(root)
|
|
123
|
+
? row.file_path.replace(/\\/g, '/').slice(root.length)
|
|
124
|
+
: row.file_path;
|
|
125
|
+
const chk = checkFile(row.file_path);
|
|
126
|
+
let icon;
|
|
127
|
+
switch (chk.status) {
|
|
128
|
+
case 'verified':
|
|
129
|
+
icon = '✅';
|
|
130
|
+
ok++;
|
|
131
|
+
break;
|
|
132
|
+
case 'tampered':
|
|
133
|
+
icon = '❌';
|
|
134
|
+
tampered++;
|
|
135
|
+
break;
|
|
136
|
+
case 'missing':
|
|
137
|
+
icon = '❓';
|
|
138
|
+
missing++;
|
|
139
|
+
break;
|
|
140
|
+
case 'unregistered':
|
|
141
|
+
icon = '🆕';
|
|
142
|
+
break;
|
|
143
|
+
default: icon = ' ';
|
|
144
|
+
}
|
|
145
|
+
console.log(`${icon} ${rel}`);
|
|
146
|
+
if (chk.status === 'tampered') {
|
|
147
|
+
console.log(` 预期: ${fmtHash(row.sha256)} 实际: ${fmtHash(chk.actual)}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
console.log(`\n摘要:通过 ${ok},已改 ${tampered},不存在 ${missing}`);
|
|
151
|
+
if (tampered > 0 || missing > 0)
|
|
152
|
+
process.exitCode = 1;
|
|
153
|
+
}
|
|
154
|
+
export async function cmdIntegrity(args) {
|
|
155
|
+
const sub = args[0];
|
|
156
|
+
if (!sub || sub === '--help' || sub === '-h') {
|
|
157
|
+
console.log(HELP);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
switch (sub) {
|
|
161
|
+
case 'check': {
|
|
162
|
+
if (!args[1]) {
|
|
163
|
+
console.error('用法: ec integrity check <path>');
|
|
164
|
+
process.exitCode = 1;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
cmdCheck(resolveTarget(args[1]));
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
case 'seal': {
|
|
171
|
+
if (!args[1]) {
|
|
172
|
+
console.error('用法: ec integrity seal <path>');
|
|
173
|
+
process.exitCode = 1;
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
cmdSeal(resolveTarget(args[1]));
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
case 'query': {
|
|
180
|
+
if (!args[1]) {
|
|
181
|
+
console.error('用法: ec integrity query <path>');
|
|
182
|
+
process.exitCode = 1;
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
cmdQuery(resolveTarget(args[1]));
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
case 'list':
|
|
189
|
+
cmdList();
|
|
190
|
+
break;
|
|
191
|
+
default:
|
|
192
|
+
console.error(`未知子命令: ${sub}`);
|
|
193
|
+
console.log(HELP);
|
|
194
|
+
process.exitCode = 1;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -21,6 +21,9 @@ const MANAGED_TASK_COMMANDS = new Set([
|
|
|
21
21
|
// `session` 按 ec 会话 id 只读查询本地 session 存档(meta 文件尾行的
|
|
22
22
|
// agentSessionId 等),不触碰 daemon 与进程生命周期。
|
|
23
23
|
'session',
|
|
24
|
+
// `getmeta` 按 ec 会话 id / threadId / agentSessionId 之一只读查询本地
|
|
25
|
+
// 会话存档,同样不触碰 daemon 与进程生命周期。
|
|
26
|
+
'getmeta',
|
|
24
27
|
// `skill` 管理 EC 技能(安装/开关/校验)。它的主要使用者就是 agent 自己——
|
|
25
28
|
// ec-skill 技能的 SKILL.md 明确指导 agent 调用它。脚本只读写
|
|
26
29
|
// `thread` 提供话题链操作,通过 thread.op IPC 鉴权
|