ec-web 0.0.2 → 0.0.4
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/dist/fs-utils.js +3 -3
- package/dist/index.js +6 -6
- package/dist/ipc-client.js +2 -2
- package/dist/paths.js +12 -12
- package/dist/process-utils.js +2 -2
- package/dist/server.js +29 -10
- package/dist/sources/aid.js +2 -2
- package/dist/sources/gateway.js +1 -1
- package/dist/sources/logs.js +2 -2
- package/dist/sources/monitor.js +90 -7
- package/dist/sources/msg.js +7 -3
- package/dist/sources/parent-package.js +8 -8
- package/dist/sources/relation-roles.js +2 -24
- package/dist/sources/role-definitions.js +16 -34
- package/dist/sources/session-codex.js +1 -1
- package/dist/sources/session.js +1 -1
- package/dist/static/app.js +142 -115
- package/dist/static/index.html +5 -5
- package/dist/static/style.css +26 -0
- package/package.json +2 -2
|
@@ -712,7 +712,7 @@ export function subscribeCodex(params, push) {
|
|
|
712
712
|
}
|
|
713
713
|
catch { }
|
|
714
714
|
}
|
|
715
|
-
// 监听
|
|
715
|
+
// 监听 evolcore sessionsDir 的 active.json 变化
|
|
716
716
|
try {
|
|
717
717
|
sessionWatcher = fs.watch(p.sessionsDir, { recursive: true }, (_evt, filename) => {
|
|
718
718
|
if (filename && String(filename).endsWith('active.json'))
|
package/dist/sources/session.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* snapshot:
|
|
9
9
|
* - 无 sessionId: 项目列表 + 选中项目的全部 transcript 概要
|
|
10
10
|
* - 有 sessionId: 该 transcript 的结构化轮次 + 会话头统计
|
|
11
|
-
* subscribe: 监听对应目录的文件变化 +
|
|
11
|
+
* subscribe: 监听对应目录的文件变化 + evolcore sessionsDir active.json 变化,防抖 150ms。
|
|
12
12
|
*/
|
|
13
13
|
import fs from 'fs';
|
|
14
14
|
import path from 'path';
|
package/dist/static/app.js
CHANGED
|
@@ -119,7 +119,7 @@ const translations = {
|
|
|
119
119
|
'logs.noSources': '未找到日志文件',
|
|
120
120
|
'logs.direction.in': 'IN',
|
|
121
121
|
'logs.direction.out': 'OUT',
|
|
122
|
-
'logs.source.
|
|
122
|
+
'logs.source.daemon': 'Daemon',
|
|
123
123
|
'logs.source.ecweb': 'ECWeb',
|
|
124
124
|
'logs.source.stdout': '标准输出',
|
|
125
125
|
'logs.source.aun': 'AUN',
|
|
@@ -374,13 +374,11 @@ const translations = {
|
|
|
374
374
|
'monitor.card.systemMemory': '系统内存',
|
|
375
375
|
'monitor.chart.cpu': 'CPU 占用',
|
|
376
376
|
'monitor.chart.memory': '内存占用',
|
|
377
|
-
'monitor.chart.
|
|
377
|
+
'monitor.chart.disk': '磁盘读写',
|
|
378
378
|
'monitor.chart.network': '网络占用',
|
|
379
|
-
'monitor.
|
|
380
|
-
'monitor.
|
|
381
|
-
'monitor.
|
|
382
|
-
'monitor.activity.interrupts': '中断',
|
|
383
|
-
'monitor.activity.toolErrors': '工具错误',
|
|
379
|
+
'monitor.series.diskRead': '读取',
|
|
380
|
+
'monitor.series.diskWrite': '写入',
|
|
381
|
+
'monitor.series.processTotal': '关联进程',
|
|
384
382
|
'monitor.series.processRss': 'Daemon 进程组 RSS',
|
|
385
383
|
'monitor.processes.daemon': '主进程',
|
|
386
384
|
'monitor.processes.ecweb': 'ECWeb',
|
|
@@ -394,7 +392,7 @@ const translations = {
|
|
|
394
392
|
'monitor.processes.sandbox': '沙箱',
|
|
395
393
|
'monitor.processes.runner-helper': 'Runner 辅助',
|
|
396
394
|
'monitor.processes.child': '关联进程',
|
|
397
|
-
'monitor.series.systemUsed': '
|
|
395
|
+
'monitor.series.systemUsed': '整机系统(已用)',
|
|
398
396
|
'monitor.series.networkDownload': '下载',
|
|
399
397
|
'monitor.series.networkUpload': '上传',
|
|
400
398
|
'monitor.th.received': '收',
|
|
@@ -452,7 +450,7 @@ const translations = {
|
|
|
452
450
|
'gateway.key.notConfigured': '未配置',
|
|
453
451
|
'gateway.key.plainTitle': '明文密钥已隐藏,建议改用 $ENV 引用',
|
|
454
452
|
'gateway.key.plainLabel': '*** (明文)',
|
|
455
|
-
'gateway.error.daemonUnavailable': '
|
|
453
|
+
'gateway.error.daemonUnavailable': 'evolcore 未运行或 socket 不可达',
|
|
456
454
|
'gateway.error.daemonFailed': 'daemon 返回失败',
|
|
457
455
|
|
|
458
456
|
// Triggers view
|
|
@@ -705,7 +703,7 @@ const translations = {
|
|
|
705
703
|
// Usage view
|
|
706
704
|
'usage.subtab.overview': '总览',
|
|
707
705
|
'usage.subtab.explorer': '详细统计',
|
|
708
|
-
'usage.overview.range.
|
|
706
|
+
'usage.overview.range.last24': '最近24小时',
|
|
709
707
|
'usage.overview.range.week': '本周',
|
|
710
708
|
'usage.overview.range.lastWeek': '上周',
|
|
711
709
|
'usage.overview.range.month': '本月',
|
|
@@ -977,7 +975,7 @@ const translations = {
|
|
|
977
975
|
'logs.noSources': 'No log files found',
|
|
978
976
|
'logs.direction.in': 'IN',
|
|
979
977
|
'logs.direction.out': 'OUT',
|
|
980
|
-
'logs.source.
|
|
978
|
+
'logs.source.daemon': 'Daemon',
|
|
981
979
|
'logs.source.ecweb': 'ECWeb',
|
|
982
980
|
'logs.source.stdout': 'Standard output',
|
|
983
981
|
'logs.source.aun': 'AUN',
|
|
@@ -1232,13 +1230,11 @@ const translations = {
|
|
|
1232
1230
|
'monitor.card.systemMemory': 'System Memory',
|
|
1233
1231
|
'monitor.chart.cpu': 'CPU Usage',
|
|
1234
1232
|
'monitor.chart.memory': 'Memory Usage',
|
|
1235
|
-
'monitor.chart.
|
|
1233
|
+
'monitor.chart.disk': 'Disk I/O',
|
|
1236
1234
|
'monitor.chart.network': 'Network Usage',
|
|
1237
|
-
'monitor.
|
|
1238
|
-
'monitor.
|
|
1239
|
-
'monitor.
|
|
1240
|
-
'monitor.activity.interrupts': 'Interrupts',
|
|
1241
|
-
'monitor.activity.toolErrors': 'ToolErr',
|
|
1235
|
+
'monitor.series.diskRead': 'Read',
|
|
1236
|
+
'monitor.series.diskWrite': 'Write',
|
|
1237
|
+
'monitor.series.processTotal': 'Related Processes',
|
|
1242
1238
|
'monitor.series.processRss': 'Daemon process group RSS',
|
|
1243
1239
|
'monitor.processes.daemon': 'Daemon',
|
|
1244
1240
|
'monitor.processes.ecweb': 'ECWeb',
|
|
@@ -1310,7 +1306,7 @@ const translations = {
|
|
|
1310
1306
|
'gateway.key.notConfigured': 'Not configured',
|
|
1311
1307
|
'gateway.key.plainTitle': 'Plaintext key is hidden; $ENV references are recommended',
|
|
1312
1308
|
'gateway.key.plainLabel': '*** (plaintext)',
|
|
1313
|
-
'gateway.error.daemonUnavailable': '
|
|
1309
|
+
'gateway.error.daemonUnavailable': 'evolcore is not running or the socket is unreachable',
|
|
1314
1310
|
'gateway.error.daemonFailed': 'daemon returned an error',
|
|
1315
1311
|
|
|
1316
1312
|
// Triggers view
|
|
@@ -1563,7 +1559,7 @@ const translations = {
|
|
|
1563
1559
|
// Usage view
|
|
1564
1560
|
'usage.subtab.overview': 'Overview',
|
|
1565
1561
|
'usage.subtab.explorer': 'Detailed Statistics',
|
|
1566
|
-
'usage.overview.range.
|
|
1562
|
+
'usage.overview.range.last24': 'Last 24 Hours',
|
|
1567
1563
|
'usage.overview.range.week': 'This Week',
|
|
1568
1564
|
'usage.overview.range.lastWeek': 'Last Week',
|
|
1569
1565
|
'usage.overview.range.month': 'This Month',
|
|
@@ -4682,7 +4678,7 @@ function renderSystem(data) {
|
|
|
4682
4678
|
|
|
4683
4679
|
// ① 版本卡
|
|
4684
4680
|
html += '<div class="cache-cards" style="margin-bottom:16px">';
|
|
4685
|
-
html += vcard('
|
|
4681
|
+
html += vcard('evolcore', sys.version, up?.evolcore);
|
|
4686
4682
|
html += vcard('FASTAUN', sys.fastaunVersion, up?.fastaun);
|
|
4687
4683
|
html += vcard('ECWEB', data.ecwebVersion, up?.ecweb ? {
|
|
4688
4684
|
remote: up.ecweb.remote,
|
|
@@ -4801,7 +4797,7 @@ function renderGateway(data) {
|
|
|
4801
4797
|
|
|
4802
4798
|
if (!data) { el.innerHTML = `<div class="empty">${t('common.loading')}</div>`; return; }
|
|
4803
4799
|
if (data.error) {
|
|
4804
|
-
const errorText = data.error === '
|
|
4800
|
+
const errorText = data.error === 'evolcore 未运行或 socket 不可达'
|
|
4805
4801
|
? t('gateway.error.daemonUnavailable')
|
|
4806
4802
|
: (data.error === 'daemon 返回失败' ? t('gateway.error.daemonFailed') : data.error);
|
|
4807
4803
|
el.innerHTML = `<div class="empty">⚠ ${esc(errorText)}</div>`;
|
|
@@ -7002,7 +6998,7 @@ function disposeWindowChart(key) {
|
|
|
7002
6998
|
}
|
|
7003
6999
|
|
|
7004
7000
|
function refreshThemeSensitiveViews() {
|
|
7005
|
-
['_hourlyChart', '_modelChart', '_monCpu', '_monMem', '
|
|
7001
|
+
['_hourlyChart', '_modelChart', '_monCpu', '_monMem', '_monDisk', '_monNet'].forEach(disposeWindowChart);
|
|
7006
7002
|
if (typeof _explorerChart !== 'undefined' && _explorerChart) {
|
|
7007
7003
|
_explorerChart.dispose();
|
|
7008
7004
|
_explorerChart = null;
|
|
@@ -7060,7 +7056,12 @@ function fmtTokens(n) {
|
|
|
7060
7056
|
}
|
|
7061
7057
|
|
|
7062
7058
|
// ── Usage Overview(总览,支持日期范围筛选)──
|
|
7063
|
-
let _ovCurrentRange = '
|
|
7059
|
+
let _ovCurrentRange = 'last24'; // 当前选择的范围
|
|
7060
|
+
|
|
7061
|
+
function rollingLast24HourRange(now) {
|
|
7062
|
+
const toTs = (now || new Date()).getTime();
|
|
7063
|
+
return { fromTs: toTs - 24 * 60 * 60 * 1000, toTs };
|
|
7064
|
+
}
|
|
7064
7065
|
|
|
7065
7066
|
async function loadUsageOverview(rangeType, customFrom, customTo) {
|
|
7066
7067
|
rangeType = rangeType || _ovCurrentRange;
|
|
@@ -7073,9 +7074,8 @@ async function loadUsageOverview(rangeType, customFrom, customTo) {
|
|
|
7073
7074
|
const todayEnd = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999).getTime();
|
|
7074
7075
|
|
|
7075
7076
|
switch (rangeType) {
|
|
7076
|
-
case '
|
|
7077
|
-
fromTs =
|
|
7078
|
-
toTs = todayEnd;
|
|
7077
|
+
case 'last24':
|
|
7078
|
+
({ fromTs, toTs } = rollingLast24HourRange(now));
|
|
7079
7079
|
break;
|
|
7080
7080
|
case 'week': // 本周(周一到今天)
|
|
7081
7081
|
const dayOfWeek = now.getDay() || 7; // 周日=7
|
|
@@ -7104,8 +7104,7 @@ async function loadUsageOverview(rangeType, customFrom, customTo) {
|
|
|
7104
7104
|
fromTs = new Date(customFrom).getTime();
|
|
7105
7105
|
toTs = new Date(customTo).getTime();
|
|
7106
7106
|
} else {
|
|
7107
|
-
fromTs =
|
|
7108
|
-
toTs = todayEnd;
|
|
7107
|
+
({ fromTs, toTs } = rollingLast24HourRange(now));
|
|
7109
7108
|
}
|
|
7110
7109
|
break;
|
|
7111
7110
|
default:
|
|
@@ -7559,7 +7558,7 @@ var _usageSubtabsBound = false;
|
|
|
7559
7558
|
|
|
7560
7559
|
function activeOverviewRange() {
|
|
7561
7560
|
const active = document.querySelector('.ov-range-btn.active');
|
|
7562
|
-
return active ? active.getAttribute('data-range') || '
|
|
7561
|
+
return active ? active.getAttribute('data-range') || 'last24' : 'last24';
|
|
7563
7562
|
}
|
|
7564
7563
|
|
|
7565
7564
|
function refreshUsageView() {
|
|
@@ -7609,16 +7608,16 @@ function initUsageSubtabs() {
|
|
|
7609
7608
|
});
|
|
7610
7609
|
});
|
|
7611
7610
|
|
|
7612
|
-
//
|
|
7611
|
+
// 初始化总览页面的过滤器并加载默认数据(最近 24 小时)
|
|
7613
7612
|
initOverviewFilters();
|
|
7614
|
-
loadUsageOverview('
|
|
7613
|
+
loadUsageOverview('last24');
|
|
7615
7614
|
}
|
|
7616
7615
|
|
|
7617
7616
|
// ── Explorer ──
|
|
7618
7617
|
var _explorerChart = null;
|
|
7619
7618
|
var _explorerInited = false;
|
|
7620
7619
|
var _expSelection = { type: null, key: null }; // { type: 'agent'|'peer', key: string } or null
|
|
7621
|
-
var _expCurrentRange = '
|
|
7620
|
+
var _expCurrentRange = 'last24'; // Explorer 当前选择的时间范围
|
|
7622
7621
|
var _expTimeRange = { fromTs: null, toTs: null }; // Explorer 的时间范围
|
|
7623
7622
|
|
|
7624
7623
|
function initExplorer() {
|
|
@@ -7653,6 +7652,7 @@ function initExplorerTimeFilters() {
|
|
|
7653
7652
|
if (customDateEl) customDateEl.style.display = 'none';
|
|
7654
7653
|
_expCurrentRange = range;
|
|
7655
7654
|
calculateExplorerTimeRange(range);
|
|
7655
|
+
setExplorerGranularityForRange(range);
|
|
7656
7656
|
loadExplorerModels(); // 加载可用模型
|
|
7657
7657
|
runExplorerQuery();
|
|
7658
7658
|
}
|
|
@@ -7675,15 +7675,22 @@ function initExplorerTimeFilters() {
|
|
|
7675
7675
|
});
|
|
7676
7676
|
}
|
|
7677
7677
|
|
|
7678
|
-
//
|
|
7678
|
+
// 设置默认时间范围(最近 24 小时)并初始化日期选择器
|
|
7679
7679
|
const now = new Date();
|
|
7680
|
+
const last24 = rollingLast24HourRange(now);
|
|
7680
7681
|
const fromEl = $('#exp-from');
|
|
7681
7682
|
const toEl = $('#exp-to');
|
|
7682
|
-
if (fromEl) fromEl.value = formatDatetimeLocal(new Date(
|
|
7683
|
+
if (fromEl) fromEl.value = formatDatetimeLocal(new Date(last24.fromTs));
|
|
7683
7684
|
if (toEl) toEl.value = formatDatetimeLocal(now);
|
|
7684
7685
|
|
|
7685
|
-
//
|
|
7686
|
-
calculateExplorerTimeRange('
|
|
7686
|
+
// 计算默认时间范围(最近 24 小时)
|
|
7687
|
+
calculateExplorerTimeRange('last24');
|
|
7688
|
+
setExplorerGranularityForRange('last24');
|
|
7689
|
+
}
|
|
7690
|
+
|
|
7691
|
+
function setExplorerGranularityForRange(rangeType) {
|
|
7692
|
+
const granularityEl = $('#exp-granularity');
|
|
7693
|
+
if (rangeType === 'last24' && granularityEl) granularityEl.value = 'hour';
|
|
7687
7694
|
}
|
|
7688
7695
|
|
|
7689
7696
|
// 计算 Explorer 的时间范围
|
|
@@ -7693,9 +7700,8 @@ function calculateExplorerTimeRange(rangeType) {
|
|
|
7693
7700
|
const todayEnd = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999).getTime();
|
|
7694
7701
|
|
|
7695
7702
|
switch (rangeType) {
|
|
7696
|
-
case '
|
|
7697
|
-
_expTimeRange
|
|
7698
|
-
_expTimeRange.toTs = todayEnd;
|
|
7703
|
+
case 'last24':
|
|
7704
|
+
_expTimeRange = rollingLast24HourRange(now);
|
|
7699
7705
|
break;
|
|
7700
7706
|
case 'week':
|
|
7701
7707
|
const dayOfWeek = now.getDay() || 7;
|
|
@@ -8108,12 +8114,12 @@ function monitorLayoutHtml() {
|
|
|
8108
8114
|
'</div>' +
|
|
8109
8115
|
`<div id="mon-cards" class="usage-cards"><div class="empty" style="grid-column:1/-1">${t('common.loading')}</div></div>` +
|
|
8110
8116
|
'<div class="mon-row2"><div id="mon-cpu-chart" class="mon-chart"></div><div id="mon-mem-chart" class="mon-chart"></div></div>' +
|
|
8111
|
-
'<div class="mon-row2"><div id="mon-
|
|
8117
|
+
'<div class="mon-row2"><div id="mon-disk-chart" class="mon-chart"></div><div id="mon-net-chart" class="mon-chart"></div></div>' +
|
|
8112
8118
|
'</div>';
|
|
8113
8119
|
}
|
|
8114
8120
|
|
|
8115
8121
|
function disposeMonitorCharts() {
|
|
8116
|
-
['_monCpu', '_monMem', '
|
|
8122
|
+
['_monCpu', '_monMem', '_monDisk', '_monNet'].forEach(disposeWindowChart);
|
|
8117
8123
|
}
|
|
8118
8124
|
|
|
8119
8125
|
function bindMonRangeTabs(wrap) {
|
|
@@ -8136,36 +8142,65 @@ function formatMonitorResponse(ms) {
|
|
|
8136
8142
|
return value >= 10000 ? (value / 1000).toFixed(1) + 's' : Math.round(value) + 'ms';
|
|
8137
8143
|
}
|
|
8138
8144
|
|
|
8139
|
-
function
|
|
8145
|
+
function monitorProcessKind(process) {
|
|
8140
8146
|
var knownKinds = ['daemon', 'ecweb', 'watch-web', 'restart-monitor', 'watch', 'watch-aid', 'codex-app-server', 'claude-runner', 'gemini-runner', 'sandbox', 'runner-helper', 'child'];
|
|
8141
|
-
|
|
8142
|
-
|
|
8147
|
+
return knownKinds.includes(process.kind) ? process.kind : 'child';
|
|
8148
|
+
}
|
|
8149
|
+
|
|
8150
|
+
function monitorProcessKey(process) {
|
|
8151
|
+
return `${monitorProcessKind(process)}\u0000${process.name || 'process'}`;
|
|
8152
|
+
}
|
|
8153
|
+
|
|
8154
|
+
function monitorProcessLabel(process) {
|
|
8155
|
+
return `${t('monitor.processes.' + monitorProcessKind(process))} · ${process.name || 'process'}`;
|
|
8143
8156
|
}
|
|
8144
8157
|
|
|
8145
|
-
function monitorProcessColor(
|
|
8158
|
+
function monitorProcessColor(key) {
|
|
8146
8159
|
var colors = ['#4f6ef7', '#38a169', '#dd6b20', '#805ad5', '#d53f8c', '#319795', '#e53e3e', '#3182ce', '#718096', '#b7791f'];
|
|
8147
|
-
var value =
|
|
8160
|
+
var value = 0;
|
|
8161
|
+
for (var i = 0; i < key.length; i += 1) value = ((value * 31) + key.charCodeAt(i)) | 0;
|
|
8162
|
+
value = Math.abs(value);
|
|
8148
8163
|
return colors[value % colors.length];
|
|
8149
8164
|
}
|
|
8150
8165
|
|
|
8151
8166
|
function monitorProcessSeries(history, currentProcesses, metric) {
|
|
8152
8167
|
var catalog = new Map();
|
|
8153
|
-
|
|
8168
|
+
function addProcess(process) {
|
|
8169
|
+
var key = monitorProcessKey(process);
|
|
8170
|
+
if (!catalog.has(key)) catalog.set(key, { key: key, kind: monitorProcessKind(process), name: process.name || 'process' });
|
|
8171
|
+
}
|
|
8172
|
+
for (const process of (currentProcesses || [])) addProcess(process);
|
|
8154
8173
|
for (const point of history) {
|
|
8155
|
-
for (const process of (point.processes || []))
|
|
8174
|
+
for (const process of (point.processes || [])) addProcess(process);
|
|
8156
8175
|
}
|
|
8157
|
-
return [...catalog.values()].sort(function (a, b) { return a.
|
|
8176
|
+
return [...catalog.values()].sort(function (a, b) { return monitorProcessLabel(a).localeCompare(monitorProcessLabel(b)); }).map(function (process) {
|
|
8158
8177
|
return {
|
|
8159
8178
|
name: monitorProcessLabel(process),
|
|
8160
8179
|
data: history.map(function (point) {
|
|
8161
|
-
var
|
|
8162
|
-
|
|
8180
|
+
var value = 0;
|
|
8181
|
+
var hasSample = false;
|
|
8182
|
+
for (const sample of (point.processes || [])) {
|
|
8183
|
+
if (monitorProcessKey(sample) !== process.key) continue;
|
|
8184
|
+
value += Number(sample[metric]) || 0;
|
|
8185
|
+
hasSample = true;
|
|
8186
|
+
}
|
|
8187
|
+
return hasSample ? value : null;
|
|
8163
8188
|
}),
|
|
8164
|
-
color: monitorProcessColor(process.
|
|
8189
|
+
color: monitorProcessColor(process.key),
|
|
8165
8190
|
};
|
|
8166
8191
|
});
|
|
8167
8192
|
}
|
|
8168
8193
|
|
|
8194
|
+
function monitorAggregateSeries(name, history, metric, color) {
|
|
8195
|
+
return {
|
|
8196
|
+
name: name,
|
|
8197
|
+
data: history.map(function (point) { return point[metric] != null ? point[metric] : null; }),
|
|
8198
|
+
color: color,
|
|
8199
|
+
width: 3,
|
|
8200
|
+
areaOpacity: 0.12,
|
|
8201
|
+
};
|
|
8202
|
+
}
|
|
8203
|
+
|
|
8169
8204
|
function renderMonitor(data) {
|
|
8170
8205
|
var wrap = $('#sys-content .mon-layout');
|
|
8171
8206
|
if (!wrap) return;
|
|
@@ -8187,6 +8222,8 @@ function renderMonitor(data) {
|
|
|
8187
8222
|
var lh = (s.stats && s.stats.lastHour) || {};
|
|
8188
8223
|
var errRate = (lh.received > 0) ? ((lh.errors / lh.received) * 100).toFixed(1) + '%' : '0%';
|
|
8189
8224
|
var processTree = s.processTree || {};
|
|
8225
|
+
var cpuCount = Math.max(1, Number(s.cpuCount) || 1);
|
|
8226
|
+
var processCpuPercent = (Number(s.cpuPercent) || 0) / cpuCount;
|
|
8190
8227
|
|
|
8191
8228
|
// ── Stat cards ──
|
|
8192
8229
|
var sysMemPct = (sys.memTotal > 0) ? Math.round((sys.memUsed / sys.memTotal) * 100) : 0;
|
|
@@ -8195,7 +8232,7 @@ function renderMonitor(data) {
|
|
|
8195
8232
|
[t('monitor.card.messages1h'), lh.received || 0],
|
|
8196
8233
|
[t('monitor.card.avgResponse'), formatMonitorResponse(lh.avgResponseMs)],
|
|
8197
8234
|
[t('monitor.card.errorRate'), errRate],
|
|
8198
|
-
[t('monitor.card.processCpu'), (
|
|
8235
|
+
[t('monitor.card.processCpu'), processCpuPercent.toFixed(1) + '%'],
|
|
8199
8236
|
[t('monitor.card.systemCpu'), (sys.cpuPercent != null ? sys.cpuPercent : 0) + '%'],
|
|
8200
8237
|
[t('monitor.card.processMemory'), fmtBytes(s.memory ? s.memory.rss : 0)],
|
|
8201
8238
|
[t('monitor.card.processCount'), processTree.processCount || 1],
|
|
@@ -8211,52 +8248,32 @@ function renderMonitor(data) {
|
|
|
8211
8248
|
var cProc = css('--accent'), cSys = css('--orange');
|
|
8212
8249
|
|
|
8213
8250
|
var currentProcesses = Array.isArray(processTree.processes) ? processTree.processes : [];
|
|
8214
|
-
var cpuSeries =
|
|
8215
|
-
|
|
8251
|
+
var cpuSeries = [
|
|
8252
|
+
monitorAggregateSeries(t('monitor.legend.system'), h, 'sysCpu', cSys),
|
|
8253
|
+
monitorAggregateSeries(t('monitor.series.processTotal'), h, 'procCpu', cProc),
|
|
8254
|
+
].concat(monitorProcessSeries(h, currentProcesses, 'cpuPercent'));
|
|
8216
8255
|
|
|
8217
|
-
// ── CPU
|
|
8256
|
+
// ── CPU:整机系统、关联进程、每类关联进程 ──
|
|
8218
8257
|
monDualLine('mon-cpu-chart', '_monCpu', ts, isDark, t('monitor.chart.cpu'),
|
|
8219
8258
|
cpuSeries,
|
|
8220
8259
|
function (v) { return Number(v).toFixed(1) + '%'; }, null);
|
|
8221
8260
|
|
|
8222
|
-
// ── Memory
|
|
8261
|
+
// ── Memory:整机系统、关联进程、每类关联进程 ──
|
|
8262
|
+
var memorySeries = [
|
|
8263
|
+
monitorAggregateSeries(t('monitor.series.systemUsed'), h, 'sysMemUsed', cSys),
|
|
8264
|
+
monitorAggregateSeries(t('monitor.series.processTotal'), h, 'procRss', cProc),
|
|
8265
|
+
].concat(monitorProcessSeries(h, currentProcesses, 'rss'));
|
|
8223
8266
|
monDualLine('mon-mem-chart', '_monMem', ts, isDark, t('monitor.chart.memory'),
|
|
8224
|
-
|
|
8267
|
+
memorySeries,
|
|
8225
8268
|
function (v) { return fmtBytes(v); }, null);
|
|
8226
8269
|
|
|
8227
|
-
// ──
|
|
8228
|
-
|
|
8229
|
-
|
|
8230
|
-
|
|
8231
|
-
|
|
8232
|
-
|
|
8233
|
-
|
|
8234
|
-
grid: { top: 36, bottom: 24, left: 44, right: 12 },
|
|
8235
|
-
xAxis: {
|
|
8236
|
-
type: 'category',
|
|
8237
|
-
data: [
|
|
8238
|
-
t('monitor.activity.received'),
|
|
8239
|
-
t('monitor.activity.completed'),
|
|
8240
|
-
t('monitor.activity.errors'),
|
|
8241
|
-
t('monitor.activity.interrupts'),
|
|
8242
|
-
t('monitor.activity.toolErrors'),
|
|
8243
|
-
],
|
|
8244
|
-
axisLabel: { fontSize: 9 },
|
|
8245
|
-
},
|
|
8246
|
-
yAxis: { type: 'value', minInterval: 1 },
|
|
8247
|
-
series: [{
|
|
8248
|
-
type: 'bar', barWidth: '45%',
|
|
8249
|
-
data: [
|
|
8250
|
-
{ value: lh.received || 0, itemStyle: { color: css('--accent') } },
|
|
8251
|
-
{ value: lh.completed || 0, itemStyle: { color: css('--green') } },
|
|
8252
|
-
{ value: lh.errors || 0, itemStyle: { color: css('--red') } },
|
|
8253
|
-
{ value: lh.interrupts || 0, itemStyle: { color: css('--orange') } },
|
|
8254
|
-
{ value: lh.toolErrors || 0, itemStyle: { color: css('--blue') } },
|
|
8255
|
-
],
|
|
8256
|
-
}],
|
|
8257
|
-
animation: false,
|
|
8258
|
-
});
|
|
8259
|
-
}
|
|
8270
|
+
// ── Disk throughput ──
|
|
8271
|
+
monDualLine('mon-disk-chart', '_monDisk', ts, isDark, t('monitor.chart.disk'),
|
|
8272
|
+
[
|
|
8273
|
+
{ name: t('monitor.series.diskRead'), data: h.map(function (p) { return p.diskReadBps || 0; }), color: css('--blue') || cProc },
|
|
8274
|
+
{ name: t('monitor.series.diskWrite'), data: h.map(function (p) { return p.diskWriteBps || 0; }), color: cSys },
|
|
8275
|
+
],
|
|
8276
|
+
function (v) { return fmtBytes(v) + '/s'; }, null);
|
|
8260
8277
|
|
|
8261
8278
|
// ── Network throughput ──
|
|
8262
8279
|
monDualLine('mon-net-chart', '_monNet', ts, isDark, t('monitor.chart.network'),
|
|
@@ -8268,7 +8285,7 @@ function renderMonitor(data) {
|
|
|
8268
8285
|
|
|
8269
8286
|
}
|
|
8270
8287
|
|
|
8271
|
-
//
|
|
8288
|
+
// 时序图。series: [{name,data,color,width?,areaOpacity?}]
|
|
8272
8289
|
function monDualLine(elId, varKey, times, isDark, title, series, fmtY, yRange) {
|
|
8273
8290
|
var el = $('#' + elId);
|
|
8274
8291
|
if (!el) return;
|
|
@@ -8307,8 +8324,8 @@ function monDualLine(elId, varKey, times, isDark, title, series, fmtY, yRange) {
|
|
|
8307
8324
|
return {
|
|
8308
8325
|
name: sr.name, type: 'line', data: sr.data, smooth: true, symbol: 'none',
|
|
8309
8326
|
connectNulls: true,
|
|
8310
|
-
lineStyle: { width: 2, color: sr.color },
|
|
8311
|
-
areaStyle: { color: sr.color, opacity: 0.08 },
|
|
8327
|
+
lineStyle: { width: sr.width || 2, color: sr.color },
|
|
8328
|
+
areaStyle: { color: sr.color, opacity: sr.areaOpacity != null ? sr.areaOpacity : 0.08 },
|
|
8312
8329
|
itemStyle: { color: sr.color },
|
|
8313
8330
|
};
|
|
8314
8331
|
}),
|
|
@@ -8769,8 +8786,7 @@ function renderRoleDefinitions(data) {
|
|
|
8769
8786
|
}
|
|
8770
8787
|
|
|
8771
8788
|
if (!data) {
|
|
8772
|
-
|
|
8773
|
-
grid.innerHTML = '<div class="role-error">❌ 未收到角色定义数据</div>';
|
|
8789
|
+
grid.innerHTML = `<div class="role-error">${esc(t('common.loading'))}</div>`;
|
|
8774
8790
|
return;
|
|
8775
8791
|
}
|
|
8776
8792
|
|
|
@@ -8877,6 +8893,8 @@ function renderDefaultRoleSelector(data) {
|
|
|
8877
8893
|
alert('保存默认角色失败' + (detail ? ': ' + detail : ''));
|
|
8878
8894
|
privateSelect.value = defaults.private;
|
|
8879
8895
|
groupSelect.value = defaults.group;
|
|
8896
|
+
} else {
|
|
8897
|
+
subscribe('roleDefinitions', roleDefinitionsParams());
|
|
8880
8898
|
}
|
|
8881
8899
|
} catch (err) {
|
|
8882
8900
|
console.error('[roleDefinitions] Failed to save defaultRoles:', err);
|
|
@@ -9154,6 +9172,7 @@ function renderCommandPermissionsList(roleDef, operations, readonly = false) {
|
|
|
9154
9172
|
|
|
9155
9173
|
function renderCommandPermissionsEditor(roleDef, operations) {
|
|
9156
9174
|
const stats = getCommandPermissionStats(roleDef?.commandPermissions);
|
|
9175
|
+
const rawPermissions = formatCommandPermissions(roleDef?.commandPermissions);
|
|
9157
9176
|
return `
|
|
9158
9177
|
<div class="form-section command-permissions-section">
|
|
9159
9178
|
<h4>Command / CLI Permissions</h4>
|
|
@@ -9168,6 +9187,18 @@ function renderCommandPermissionsEditor(roleDef, operations) {
|
|
|
9168
9187
|
? '清单来自后端 operation registry。每行显示当前有效权限和命中的规则;修改后会写入精确 operation 规则。若要撤销内置继承规则,请对同名 operation 设置“拒绝”。'
|
|
9169
9188
|
: 'The list comes from the backend operation registry. Each row shows the current effective permission and matched rule. Changes are saved as exact operation rules.'}
|
|
9170
9189
|
</div>
|
|
9190
|
+
<details class="cli-raw-editor">
|
|
9191
|
+
<summary>${currentLang === 'zh-CN' ? '编辑原始规则 JSON' : 'Edit raw rules JSON'}</summary>
|
|
9192
|
+
<div class="cli-help">
|
|
9193
|
+
${currentLang === 'zh-CN'
|
|
9194
|
+
? '用于编辑通配符或未出现在 operation 清单中的规则。下方结构化修改会合并到这份 JSON。'
|
|
9195
|
+
: 'Use this for wildcard rules or operations missing from the catalog. Structured changes below are merged into this JSON.'}
|
|
9196
|
+
</div>
|
|
9197
|
+
<textarea id="edit-commandPermissions"
|
|
9198
|
+
class="form-textarea cli-raw-textarea"
|
|
9199
|
+
rows="12"
|
|
9200
|
+
spellcheck="false">${esc(rawPermissions)}</textarea>
|
|
9201
|
+
</details>
|
|
9171
9202
|
${renderCommandPermissionsList(roleDef, operations, false)}
|
|
9172
9203
|
</div>
|
|
9173
9204
|
`;
|
|
@@ -9209,21 +9240,24 @@ function validateCommandPermissionsClient(commandPermissions) {
|
|
|
9209
9240
|
|
|
9210
9241
|
function collectCommandPermissions(container, fallback = {}) {
|
|
9211
9242
|
const rows = Array.from(container.querySelectorAll('[data-command-operation]'));
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
|
|
9243
|
+
const input = container.querySelector('#edit-commandPermissions');
|
|
9244
|
+
let commandPermissions = getCommandPermissionsObject(fallback);
|
|
9245
|
+
|
|
9246
|
+
if (input) {
|
|
9215
9247
|
try {
|
|
9216
9248
|
const parsed = JSON.parse(input.value.trim() || '{}');
|
|
9217
9249
|
const validationError = validateCommandPermissionsClient(parsed);
|
|
9218
9250
|
if (validationError) return { ok: false, error: validationError };
|
|
9219
|
-
|
|
9251
|
+
commandPermissions = parsed;
|
|
9220
9252
|
} catch (err) {
|
|
9221
9253
|
return { ok: false, error: `Command permissions JSON is invalid: ${err.message}` };
|
|
9222
9254
|
}
|
|
9223
9255
|
}
|
|
9224
9256
|
|
|
9257
|
+
if (rows.length === 0) return { ok: true, value: commandPermissions };
|
|
9258
|
+
|
|
9225
9259
|
const operationsById = new Map((roleOperationsCache || []).map(operation => [operation.id, operation]));
|
|
9226
|
-
|
|
9260
|
+
commandPermissions = { ...commandPermissions };
|
|
9227
9261
|
|
|
9228
9262
|
for (const row of rows) {
|
|
9229
9263
|
const operationId = row.dataset.commandOperation;
|
|
@@ -9435,7 +9469,7 @@ function createRoleCard(roleName, roleDef) {
|
|
|
9435
9469
|
const commandStats = getCommandPermissionStats(roleDef.commandPermissions);
|
|
9436
9470
|
|
|
9437
9471
|
// 判断是否为内置角色
|
|
9438
|
-
const builtinRoles = ['member', 'visitor'];
|
|
9472
|
+
const builtinRoles = ['admin', 'member', 'visitor'];
|
|
9439
9473
|
const isBuiltin = builtinRoles.includes(roleName);
|
|
9440
9474
|
|
|
9441
9475
|
card.innerHTML = `
|
|
@@ -9530,6 +9564,7 @@ function initRoleDefinitionsTab() {
|
|
|
9530
9564
|
alert(t('roleDefs.saveFailed'));
|
|
9531
9565
|
} else {
|
|
9532
9566
|
alert(t('roleDefs.saveSuccess'));
|
|
9567
|
+
subscribe('roleDefinitions', roleDefinitionsParams());
|
|
9533
9568
|
}
|
|
9534
9569
|
} catch (err) {
|
|
9535
9570
|
alert(t('pair.error.network'));
|
|
@@ -9575,6 +9610,7 @@ function initRoleDefinitionsTab() {
|
|
|
9575
9610
|
alert(t('roleDefs.saveFailed') + ': ' + (err.error || 'unknown'));
|
|
9576
9611
|
} else {
|
|
9577
9612
|
alert('角色已删除');
|
|
9613
|
+
subscribe('roleDefinitions', roleDefinitionsParams());
|
|
9578
9614
|
}
|
|
9579
9615
|
} catch (err) {
|
|
9580
9616
|
alert(t('pair.error.network'));
|
|
@@ -10244,14 +10280,8 @@ async function showRoleEditModal(roleName, roleDef) {
|
|
|
10244
10280
|
formHtml += renderRoleUsageLimitsEditor(roleDef);
|
|
10245
10281
|
|
|
10246
10282
|
formHtml += `
|
|
10247
|
-
<div class="
|
|
10248
|
-
<
|
|
10249
|
-
<button type="button" class="role-editor-tab" data-role-tab="cli">CLI Permissions</button>
|
|
10250
|
-
</div>
|
|
10251
|
-
|
|
10252
|
-
<div class="role-editor-tab-panel active" data-role-tab-panel="fields">
|
|
10253
|
-
<div class="form-section">
|
|
10254
|
-
<h4>权限配置</h4>
|
|
10283
|
+
<div class="form-section field-permissions-section">
|
|
10284
|
+
<h4>Field Permissions</h4>
|
|
10255
10285
|
`;
|
|
10256
10286
|
|
|
10257
10287
|
const permissions = roleDef.permissions || {};
|
|
@@ -10421,16 +10451,13 @@ async function showRoleEditModal(roleName, roleDef) {
|
|
|
10421
10451
|
formHtml += `</div>`;
|
|
10422
10452
|
});
|
|
10423
10453
|
|
|
10424
|
-
formHtml += `</div
|
|
10425
|
-
|
|
10426
|
-
${renderCommandPermissionsEditor(roleDef, operations)}
|
|
10427
|
-
</div>
|
|
10454
|
+
formHtml += `</div>
|
|
10455
|
+
${renderCommandPermissionsEditor(roleDef, operations)}
|
|
10428
10456
|
</div>`;
|
|
10429
10457
|
|
|
10430
10458
|
body.innerHTML = formHtml;
|
|
10431
10459
|
|
|
10432
10460
|
bindTagsInputEvents(body);
|
|
10433
|
-
initRoleEditorTabs(body);
|
|
10434
10461
|
initCommandPermissionEditor(body);
|
|
10435
10462
|
initRoleModelPermissionEditor(body, roleName, roleDef);
|
|
10436
10463
|
|
package/dist/static/index.html
CHANGED
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
|
|
48
48
|
</nav>
|
|
49
49
|
<span style="flex:1"></span>
|
|
50
|
-
2026-07-
|
|
50
|
+
2026-07-25 03:26
|
|
51
51
|
<span id="conn-status" class="conn-status" data-i18n="status.connecting">连接中…</span>
|
|
52
52
|
<button id="lang-btn" class="theme-btn" data-i18n-title="language.buttonTitle" title="切换语言">🌐</button>
|
|
53
53
|
<button id="theme-btn" class="theme-btn" data-i18n-title="theme.buttonTitle" title="主题">🌗</button>
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
<div id="usage-overview" class="usage-subpanel active">
|
|
142
142
|
<div class="overview-filters">
|
|
143
143
|
<div class="ov-range-tabs">
|
|
144
|
-
<button class="ov-range-btn active" data-range="
|
|
144
|
+
<button class="ov-range-btn active" data-range="last24" data-i18n="usage.overview.range.last24">Last 24 Hours</button>
|
|
145
145
|
<button class="ov-range-btn" data-range="week" data-i18n="usage.overview.range.week">This Week</button>
|
|
146
146
|
<button class="ov-range-btn" data-range="lastWeek" data-i18n="usage.overview.range.lastWeek">Last Week</button>
|
|
147
147
|
<button class="ov-range-btn" data-range="month" data-i18n="usage.overview.range.month">This Month</button>
|
|
@@ -218,7 +218,7 @@
|
|
|
218
218
|
<!-- 时间范围选择(与总览类似,但支持精确到时分秒) -->
|
|
219
219
|
<div class="exp-time-filters">
|
|
220
220
|
<div class="exp-range-tabs">
|
|
221
|
-
<button class="exp-range-btn active" data-range="
|
|
221
|
+
<button class="exp-range-btn active" data-range="last24" data-i18n="usage.overview.range.last24">Last 24 Hours</button>
|
|
222
222
|
<button class="exp-range-btn" data-range="week" data-i18n="usage.overview.range.week">This Week</button>
|
|
223
223
|
<button class="exp-range-btn" data-range="lastWeek" data-i18n="usage.overview.range.lastWeek">Last Week</button>
|
|
224
224
|
<button class="exp-range-btn" data-range="month" data-i18n="usage.overview.range.month">This Month</button>
|
|
@@ -245,8 +245,8 @@
|
|
|
245
245
|
<label>
|
|
246
246
|
<span data-i18n="usage.explorer.filter.granularity">Granularity</span>
|
|
247
247
|
<select id="exp-granularity">
|
|
248
|
-
<option value="hour" data-i18n="usage.explorer.filter.granularity.hour">Hour</option>
|
|
249
|
-
<option value="day"
|
|
248
|
+
<option value="hour" selected data-i18n="usage.explorer.filter.granularity.hour">Hour</option>
|
|
249
|
+
<option value="day" data-i18n="usage.explorer.filter.granularity.day">Day</option>
|
|
250
250
|
<option value="week" data-i18n="usage.explorer.filter.granularity.week">Week</option>
|
|
251
251
|
<option value="month" data-i18n="usage.explorer.filter.granularity.month">Month</option>
|
|
252
252
|
</select>
|
package/dist/static/style.css
CHANGED
|
@@ -3643,6 +3643,32 @@ tr.ag-disabled .ag-name { color: var(--dim); }
|
|
|
3643
3643
|
line-height: 1.5;
|
|
3644
3644
|
}
|
|
3645
3645
|
|
|
3646
|
+
.cli-raw-editor {
|
|
3647
|
+
border: 1px solid var(--border);
|
|
3648
|
+
border-radius: 6px;
|
|
3649
|
+
background: var(--bg2);
|
|
3650
|
+
padding: 10px 12px;
|
|
3651
|
+
}
|
|
3652
|
+
|
|
3653
|
+
.cli-raw-editor summary {
|
|
3654
|
+
color: var(--fg);
|
|
3655
|
+
cursor: pointer;
|
|
3656
|
+
font-size: 13px;
|
|
3657
|
+
font-weight: 600;
|
|
3658
|
+
}
|
|
3659
|
+
|
|
3660
|
+
.cli-raw-editor[open] summary {
|
|
3661
|
+
margin-bottom: 8px;
|
|
3662
|
+
}
|
|
3663
|
+
|
|
3664
|
+
.cli-raw-textarea {
|
|
3665
|
+
margin-top: 10px;
|
|
3666
|
+
min-height: 180px;
|
|
3667
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
|
3668
|
+
font-size: 12px;
|
|
3669
|
+
line-height: 1.5;
|
|
3670
|
+
}
|
|
3671
|
+
|
|
3646
3672
|
.cli-category-section {
|
|
3647
3673
|
display: flex;
|
|
3648
3674
|
flex-direction: column;
|