ec-web 0.0.10 → 0.0.11

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.
@@ -7,7 +7,22 @@
7
7
  * watch 用到的只读查询:aun-aids / aun-aid-stats / status / evolagent.list / ping
8
8
  */
9
9
  import net from 'net';
10
+ import fs from 'fs';
11
+ import path from 'path';
12
+ const LOCAL_CONTROL_COMMANDS = new Set(['menu.exec', 'monitor-snapshot']);
10
13
  export function ipcQuery(socketPath, cmd, timeoutMs = 3000) {
14
+ let request = cmd;
15
+ if (LOCAL_CONTROL_COMMANDS.has(cmd.type) && typeof cmd.controlToken !== 'string') {
16
+ try {
17
+ const tokenPath = process.platform === 'win32'
18
+ ? path.join(process.env.EVOLCORE_HOME || '', 'data', 'instance', 'control.token')
19
+ : path.join(path.dirname(socketPath), 'control.token');
20
+ const controlToken = fs.readFileSync(tokenPath, 'utf8').trim();
21
+ if (controlToken)
22
+ request = { ...cmd, controlToken };
23
+ }
24
+ catch { }
25
+ }
11
26
  return new Promise((resolve) => {
12
27
  const conn = net.connect(socketPath);
13
28
  let buf = '';
@@ -16,7 +31,7 @@ export function ipcQuery(socketPath, cmd, timeoutMs = 3000) {
16
31
  resolve(null);
17
32
  }, timeoutMs);
18
33
  conn.on('connect', () => {
19
- conn.write(JSON.stringify(cmd) + '\n');
34
+ conn.write(JSON.stringify(request) + '\n');
20
35
  });
21
36
  conn.on('data', (data) => {
22
37
  buf += data.toString();
@@ -430,6 +430,18 @@ function toolOutputText(output) {
430
430
  return String(output);
431
431
  }
432
432
  }
433
+ function classifyCodexToolRejection(text) {
434
+ const normalized = text.replace(/\\(["'])/g, '$1');
435
+ if (!/Rejected\(\s*["']rejected by user["']\s*\)/i.test(normalized))
436
+ return undefined;
437
+ const isManagedEcSend = /(?:^|[/'\s])ec\s+(?:msg|group)\s+send\b/i.test(text);
438
+ return {
439
+ kind: 'approval_rejected',
440
+ source: isManagedEcSend ? 'evolcore-policy' : 'codex-approval',
441
+ reason: 'rejected_by_user',
442
+ executed: false,
443
+ };
444
+ }
433
445
  export function parseCodexToolEvent(event) {
434
446
  if (!event || (event.type !== 'event_msg' && event.type !== 'response_item') || !event.payload)
435
447
  return null;
@@ -457,6 +469,8 @@ export function parseCodexToolEvent(event) {
457
469
  };
458
470
  }
459
471
  if (payload.type === 'function_call_output' || payload.type === 'custom_tool_call_output') {
472
+ const text = toolOutputText(payload.output);
473
+ const rejection = classifyCodexToolRejection(text);
460
474
  return {
461
475
  kind: 'result',
462
476
  callId,
@@ -467,8 +481,9 @@ export function parseCodexToolEvent(event) {
467
481
  category: 'tool_result',
468
482
  blocks: [{
469
483
  kind: 'tool_result',
470
- text: toolOutputText(payload.output),
471
- isError: payload.is_error === true || payload.status === 'failed',
484
+ text,
485
+ isError: payload.is_error === true || payload.status === 'failed' || !!rejection,
486
+ ...(rejection ? { rejection } : {}),
472
487
  }],
473
488
  },
474
489
  };
@@ -342,6 +342,11 @@ const translations = {
342
342
  'sessions.block.thinking': '思考',
343
343
  'sessions.block.result': '结果',
344
344
  'sessions.block.resultError': '结果',
345
+ 'sessions.block.rejection.title': '已拦截',
346
+ 'sessions.block.rejection.source.policy': 'EvolCore 策略自动拦截',
347
+ 'sessions.block.rejection.source.approval': '审批通道拒绝',
348
+ 'sessions.block.rejection.notExecuted': '命令未执行',
349
+ 'sessions.block.rejection.reason': '底层返回:Rejected("rejected by user")',
345
350
  'sessions.content.expand': '展开内容',
346
351
  'sessions.content.collapse': '收起内容',
347
352
  'sessions.contextTitle': '最后一轮喂给模型的完整上下文大小',
@@ -1230,6 +1235,11 @@ const translations = {
1230
1235
  'sessions.block.thinking': 'Thinking',
1231
1236
  'sessions.block.result': 'Result',
1232
1237
  'sessions.block.resultError': 'Result',
1238
+ 'sessions.block.rejection.title': 'Intercepted',
1239
+ 'sessions.block.rejection.source.policy': 'EvolCore policy auto-blocked',
1240
+ 'sessions.block.rejection.source.approval': 'Approval channel rejected',
1241
+ 'sessions.block.rejection.notExecuted': 'Command was not executed',
1242
+ 'sessions.block.rejection.reason': 'Backend response: Rejected("rejected by user")',
1233
1243
  'sessions.content.expand': 'Expand content',
1234
1244
  'sessions.content.collapse': 'Collapse content',
1235
1245
  'sessions.contextTitle': 'Full context size sent to the model in the last turn',
@@ -3682,11 +3692,23 @@ function renderBlocks(blocks, options = {}) {
3682
3692
  : '';
3683
3693
  out += `<details class="blk blk-tool" open><summary class="tool-head">${icon} <span class="tool-name">${esc(tool)}</span></summary>${toolParams}</details>`;
3684
3694
  } else if (b.kind === 'tool_result') {
3685
- const cls = b.isError ? 'blk-result err' : 'blk-result';
3695
+ const rejection = b.rejection?.kind === 'approval_rejected' ? b.rejection : undefined;
3696
+ const cls = b.isError ? `blk-result err${rejection ? ' rejection' : ''}` : 'blk-result';
3686
3697
  const loadFull = b.truncated && b.resultKey
3687
3698
  ? `<button class="ctrl-btn result-load-full" type="button" data-result-key="${esc(b.resultKey)}">${t('sessions.loadFullResult') || '加载完整结果'}</button>`
3688
3699
  : '';
3689
- out += `<details class="blk ${cls}" open><summary>${b.isError ? '✗ ' + t('sessions.block.resultError') : '↳ ' + t('sessions.block.result')}</summary>` +
3700
+ const rejectionMeta = rejection
3701
+ ? '<div class="result-rejection-meta">' +
3702
+ `<span class="result-rejection-badge">⚠ ${t('sessions.block.rejection.title')}</span>` +
3703
+ `<span>${rejection.source === 'evolcore-policy' ? t('sessions.block.rejection.source.policy') : t('sessions.block.rejection.source.approval')}</span>` +
3704
+ `<span>${t('sessions.block.rejection.notExecuted')}</span>` +
3705
+ `<span class="result-rejection-reason">${t('sessions.block.rejection.reason')}</span>` +
3706
+ '</div>'
3707
+ : '';
3708
+ const resultLabel = rejection
3709
+ ? t('sessions.block.rejection.title')
3710
+ : b.isError ? t('sessions.block.resultError') : t('sessions.block.result');
3711
+ out += `<details class="blk ${cls}" open><summary>${b.isError ? '✗ ' : '↳ '}${resultLabel}</summary>${rejectionMeta}` +
3690
3712
  `<div class="result-content collapse-content"><pre class="result-body collapse-body">${esc(b.text)}</pre>${loadFull}<button class="collapse-arrow-toggle" type="button" hidden aria-expanded="false"><span class="collapse-arrow-icon" aria-hidden="true"></span></button></div></details>`;
3691
3713
  } else if (b.kind === 'subtask') {
3692
3714
  const knownStatuses = ['running', 'completed', 'failed', 'stopped', 'killed'];
@@ -48,7 +48,7 @@
48
48
 
49
49
  </nav>
50
50
  <span style="flex:1"></span>
51
- 2026-08-11 03:04
51
+ 2026-08-19 05:07
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>
@@ -540,6 +540,17 @@ tr:hover td { background: var(--bg2); }
540
540
  .blk-result { font-size: 12px; }
541
541
  .blk-result summary { cursor: pointer; color: var(--dim); }
542
542
  .blk-result.err summary { color: var(--red); }
543
+ .result-rejection-meta {
544
+ display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px;
545
+ margin-top: 7px; color: var(--dim); font-size: 11px;
546
+ }
547
+ .result-rejection-badge {
548
+ display: inline-flex; align-items: center; gap: 4px;
549
+ padding: 2px 7px; border: 1px solid color-mix(in srgb, var(--red) 45%, var(--border));
550
+ border-radius: 4px; color: var(--red); background: color-mix(in srgb, var(--red) 8%, transparent);
551
+ font-weight: 600;
552
+ }
553
+ .result-rejection-reason { flex-basis: 100%; overflow-wrap: anywhere; }
543
554
  .collapse-content { position: relative; }
544
555
  .result-content { --collapse-fade-bg: var(--bg); margin-top: 6px; }
545
556
  .collapse-content::after {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ec-web",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "description": "Web console and monitoring dashboard for EvolCore",
5
5
  "type": "module",
6
6
  "bin": {