ec-web 0.0.17 → 0.0.19

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.
@@ -9,7 +9,10 @@
9
9
  import net from 'net';
10
10
  import fs from 'fs';
11
11
  import path from 'path';
12
- const LOCAL_CONTROL_COMMANDS = new Set(['menu.exec', 'monitor-snapshot']);
12
+ // Keep this list aligned with evolcore's local-control IPC gate. `evolagent.list`
13
+ // is used by the Agents view and is read-only from EC Web's perspective, but the
14
+ // daemon still requires the local control token before returning its registry.
15
+ const LOCAL_CONTROL_COMMANDS = new Set(['evolagent.list', 'menu.exec', 'monitor-snapshot']);
13
16
  export function ipcQuery(socketPath, cmd, timeoutMs = 3000) {
14
17
  let request = cmd;
15
18
  if (LOCAL_CONTROL_COMMANDS.has(cmd.type) && typeof cmd.controlToken !== 'string') {
package/dist/paths.js CHANGED
@@ -7,12 +7,10 @@
7
7
  * 根目录解析优先级:
8
8
  * 1. --home 命令行参数(由 index.ts 写入 process.env.EVOLCORE_HOME)
9
9
  * 2. EVOLCORE_HOME 环境变量
10
- * 3. cwd/agents/defaults.json 存在 → cwd(开发模式)
11
- * 4. ~/.evolcore
10
+ * 3. ~/.evolcore
12
11
  */
13
12
  import path from 'path';
14
13
  import os from 'os';
15
- import fs from 'fs';
16
14
  import crypto from 'crypto';
17
15
  const isWindows = process.platform === 'win32';
18
16
  let _root = null;
@@ -22,9 +20,6 @@ export function resolveRoot() {
22
20
  if (process.env.EVOLCORE_HOME) {
23
21
  _root = process.env.EVOLCORE_HOME;
24
22
  }
25
- else if (fs.existsSync(path.join(process.cwd(), 'agents', 'defaults.json'))) {
26
- _root = process.cwd();
27
- }
28
23
  else {
29
24
  _root = path.join(os.homedir(), '.evolcore');
30
25
  }
@@ -14,12 +14,17 @@ const LOG_SOURCES = [
14
14
  { id: 'triggers', label: 'Trigger runs', pattern: /^trigger-runs(?:-.+)?\.(?:log|jsonl)$/ },
15
15
  { id: 'restart', label: 'Restart', pattern: /^restart\.log$/ },
16
16
  { id: 'menu-cli', label: 'Menu CLI', pattern: /^menu-cli-exec\.log$/ },
17
+ { id: 'menu-protocol', label: 'Menu Protocol', pattern: /^menu-protocol(?:-.+)?\.log$/ },
17
18
  { id: 'causation', label: 'Causation spans', pattern: /^causation-spans(?:-.+)?\.log$/ },
19
+ { id: 'command-audit', label: 'Command audit', pattern: /^command-audit(?:-.+)?\.log$/ },
20
+ { id: 'role-mutations', label: 'Role mutations', pattern: /^role-mutations(?:-.+)?\.log$/ },
18
21
  ];
19
22
  const DEFAULT_LIMIT = 500;
20
- const MAX_LIMIT = 1000;
21
- const MAX_FILE_TAIL_BYTES = 384 * 1024;
23
+ const MAX_LIMIT = 2000;
24
+ const TAIL_READ_CHUNK_BYTES = 256 * 1024;
25
+ const MAX_FILE_TAIL_BYTES = 32 * 1024 * 1024;
22
26
  const MAX_LINE_CHARS = 12_000;
27
+ const MAX_JSON_PARSE_CHARS = 4 * 1024 * 1024;
23
28
  function listLogFiles() {
24
29
  const logsDir = resolvePaths().logs;
25
30
  try {
@@ -55,12 +60,30 @@ function availableSources(files) {
55
60
  function readTail(file, maxLines) {
56
61
  if (file.size <= 0 || maxLines <= 0)
57
62
  return [];
58
- const bytes = Math.min(file.size, MAX_FILE_TAIL_BYTES);
59
- const buffer = Buffer.allocUnsafe(bytes);
60
63
  let handle = null;
61
64
  try {
62
65
  handle = fs.openSync(file.path, 'r');
63
- fs.readSync(handle, buffer, 0, bytes, file.size - bytes);
66
+ const chunks = [];
67
+ let position = file.size;
68
+ let bytesRead = 0;
69
+ let newlineCount = 0;
70
+ while (position > 0 && bytesRead < MAX_FILE_TAIL_BYTES && newlineCount <= maxLines) {
71
+ const chunkSize = Math.min(TAIL_READ_CHUNK_BYTES, position, MAX_FILE_TAIL_BYTES - bytesRead);
72
+ const chunk = Buffer.allocUnsafe(chunkSize);
73
+ position -= chunkSize;
74
+ fs.readSync(handle, chunk, 0, chunkSize, position);
75
+ chunks.unshift(chunk);
76
+ bytesRead += chunkSize;
77
+ for (const byte of chunk)
78
+ if (byte === 0x0a)
79
+ newlineCount += 1;
80
+ }
81
+ let text = Buffer.concat(chunks).toString('utf8');
82
+ if (position > 0) {
83
+ const firstBreak = text.indexOf('\n');
84
+ text = firstBreak >= 0 ? text.slice(firstBreak + 1) : '';
85
+ }
86
+ return text.split(/\r?\n/).filter(Boolean).slice(-maxLines);
64
87
  }
65
88
  catch {
66
89
  return [];
@@ -72,12 +95,6 @@ function readTail(file, maxLines) {
72
95
  }
73
96
  catch { }
74
97
  }
75
- let text = buffer.toString('utf8');
76
- if (bytes < file.size) {
77
- const firstBreak = text.indexOf('\n');
78
- text = firstBreak >= 0 ? text.slice(firstBreak + 1) : '';
79
- }
80
- return text.split(/\r?\n/).filter(Boolean).slice(-maxLines);
81
98
  }
82
99
  function shortLogName(file) {
83
100
  return path.basename(file)
@@ -175,6 +192,28 @@ function parseJsonLine(raw, file) {
175
192
  raw,
176
193
  };
177
194
  }
195
+ if (typeof value.event === 'string' && value.event.trim()) {
196
+ const event = value.event.trim();
197
+ if (!level) {
198
+ level = event.endsWith('.failed') || event.endsWith('.error')
199
+ ? 'error'
200
+ : (event.endsWith('.completed') ? 'info' : 'debug');
201
+ }
202
+ return {
203
+ timestamp,
204
+ level,
205
+ file,
206
+ module: event.split(/[.:]/)[0] || shortLogName(file),
207
+ actor: value.selfAid ? String(value.selfAid) : (value.self_aid ? String(value.self_aid) : null),
208
+ direction: null,
209
+ event,
210
+ fields: collectFields(value, new Set(['ts', 'timestamp', 'event', 'selfAid', 'self_aid']), 10),
211
+ format: 'json',
212
+ userMessage: false,
213
+ message: '',
214
+ raw,
215
+ };
216
+ }
178
217
  if (value.runId && value.triggerId) {
179
218
  const startedAt = Number(value.startedAt);
180
219
  const finishedAt = Number(value.finishedAt);
@@ -328,7 +367,12 @@ function parseLine(rawLine, file) {
328
367
  const raw = rawLine.length > MAX_LINE_CHARS
329
368
  ? `${rawLine.slice(0, MAX_LINE_CHARS)} …`
330
369
  : rawLine;
331
- return raw.startsWith('{') ? (parseJsonLine(raw, file) || parseTextLine(raw, file)) : parseTextLine(raw, file);
370
+ if (rawLine.startsWith('{') && rawLine.length <= MAX_JSON_PARSE_CHARS) {
371
+ const parsed = parseJsonLine(rawLine, file);
372
+ if (parsed)
373
+ return { ...parsed, raw };
374
+ }
375
+ return parseTextLine(raw, file);
332
376
  }
333
377
  function linesFor(files, source, limit) {
334
378
  const matching = files
@@ -108,9 +108,9 @@ const translations = {
108
108
  'logs.search': '搜索',
109
109
  'logs.searchPlaceholder': '筛选当前日志',
110
110
  'logs.lines': '行数',
111
- 'logs.lines.200': '200 行',
112
111
  'logs.lines.500': '500 行',
113
112
  'logs.lines.1000': '1000 行',
113
+ 'logs.lines.2000': '2000 行',
114
114
  'logs.follow': '跟随尾部',
115
115
  'logs.live': '自动刷新',
116
116
  'logs.meta': '{files} 个文件 · {size} · 更新于 {time}',
@@ -131,7 +131,10 @@ const translations = {
131
131
  'logs.source.triggers': '触发器运行',
132
132
  'logs.source.restart': '重启',
133
133
  'logs.source.menu-cli': '菜单 CLI',
134
+ 'logs.source.menu-protocol': 'Menu 协议',
134
135
  'logs.source.causation': '因果链路',
136
+ 'logs.source.command-audit': '命令审计',
137
+ 'logs.source.role-mutations': '角色变更',
135
138
 
136
139
  // Agents view
137
140
  'agents.subtitle.enabled': '启用',
@@ -1011,9 +1014,9 @@ const translations = {
1011
1014
  'logs.search': 'Search',
1012
1015
  'logs.searchPlaceholder': 'Filter current logs',
1013
1016
  'logs.lines': 'Lines',
1014
- 'logs.lines.200': '200 Lines',
1015
1017
  'logs.lines.500': '500 Lines',
1016
1018
  'logs.lines.1000': '1000 Lines',
1019
+ 'logs.lines.2000': '2000 Lines',
1017
1020
  'logs.follow': 'Follow tail',
1018
1021
  'logs.live': 'Auto refresh',
1019
1022
  'logs.meta': '{files} files · {size} · updated {time}',
@@ -1034,7 +1037,10 @@ const translations = {
1034
1037
  'logs.source.triggers': 'Trigger runs',
1035
1038
  'logs.source.restart': 'Restart',
1036
1039
  'logs.source.menu-cli': 'Menu CLI',
1040
+ 'logs.source.menu-protocol': 'Menu Protocol',
1037
1041
  'logs.source.causation': 'Causation spans',
1042
+ 'logs.source.command-audit': 'Command audit',
1043
+ 'logs.source.role-mutations': 'Role mutations',
1038
1044
 
1039
1045
  // Agents view
1040
1046
  'agents.subtitle.enabled': 'Enabled',
@@ -48,7 +48,7 @@
48
48
 
49
49
  </nav>
50
50
  <span style="flex:1"></span>
51
- 2026-08-25 20:02
51
+ 2026-08-27 15:27
52
52
  <span id="conn-status" class="conn-status" data-i18n="status.connecting">连接中…</span>
53
53
  <button id="lang-btn" class="theme-btn" data-i18n-title="language.buttonTitle" title="切换语言">🌐</button>
54
54
  <button id="theme-btn" class="theme-btn" data-i18n-title="theme.buttonTitle" title="主题">🌗</button>
@@ -109,9 +109,9 @@
109
109
  <label class="log-control log-limit-control">
110
110
  <span class="visually-hidden" data-i18n="logs.lines">行数</span>
111
111
  <select id="log-limit">
112
- <option value="200" data-i18n="logs.lines.200">200 行</option>
113
112
  <option value="500" data-i18n="logs.lines.500" selected>500 行</option>
114
113
  <option value="1000" data-i18n="logs.lines.1000">1000 行</option>
114
+ <option value="2000" data-i18n="logs.lines.2000">2000 行</option>
115
115
  </select>
116
116
  </label>
117
117
  <label class="log-follow">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ec-web",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "description": "Web console and monitoring dashboard for EvolCore",
5
5
  "type": "module",
6
6
  "bin": {