feat: F健康cron来源标记+开发原则横幅——pipelines加source字段(hermes/system_crontab),扫描系统crontab的MoFin任务标红'系统cron(违规)',顶部横幅'禁止系统crontab部署自动任务统一hermes'+违规统计

This commit is contained in:
hmo
2026-08-12 15:19:09 +08:00
parent 6460437bb4
commit bd010004ea
2 changed files with 71 additions and 3 deletions
+53
View File
@@ -132,10 +132,51 @@ def load_cron_jobs():
if jid in seen: continue
seen.add(jid)
j["profile"] = profile_tag
j["source"] = "hermes" # 2026-08-12 cron来源标记:统一 hermes 体系
jobs.append(j)
except: pass
return jobs
def load_system_crontab_tasks():
"""扫描系统 crontab 里 MoFin 相关任务(2026-08-12 老莫要求:显著提示违规系统 cron)。
开发原则:自动任务必须部署在 hermesposition-analyst),禁止系统 crontab。
返回含 MoFin/profile-scripts 路径的 crontab 条目,source='system_crontab'(违规警告)。"""
import subprocess as _sp
tasks = []
try:
out = _sp.run(["crontab", "-l"], capture_output=True, text=True, timeout=10).stdout
for line in out.splitlines():
line = line.strip()
if not line or line.startswith("#"):
continue
# 只关心 MoFin 相关(含 MoFin 或 profile-scripts 路径)
if "MoFin" not in line and "profile-scripts" not in line:
continue
# 提取调度(前 5 个字段或 @reboot)和命令/脚本
script = ""
m = line.split("python3")[-1].split("python")[-1].strip()
import re as _re
sm = _re.search(r'([\w./-]*profile-scripts/([\w.-]+\.py)|([\w.-]+\.py))', line)
if sm:
script = sm.group(2) or sm.group(3) or ""
sched = " ".join(line.split()[:5]) if not line.startswith("@") else line.split()[0]
tasks.append({
"name": f"[系统cron] {script or line[:40]}",
"script": script,
"schedule": sched,
"status": "unknown",
"last_run": "",
"profile": "system",
"source": "system_crontab", # 违规:应迁移到 hermes
"no_agent": True,
"type": "no_agent",
"enabled": True,
})
except Exception:
pass
return tasks
def get_db_stats():
conn = get_conn()
tables = conn.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name").fetchall()
@@ -1018,6 +1059,7 @@ def build_report():
"type": "no_agent" if no_agent else "LLM",
"script": script,
"layer": _pipeline_layer(script, name), # 2026-08-12 三层架构层归属
"source": j.get("source", "hermes"), # 2026-08-12 cron来源(hermes 统一体系)
"schedule": schedule,
"status": status,
"last_run": last_run,
@@ -1029,6 +1071,17 @@ def build_report():
feature_tree["status"] = "fail"
feature_tree.setdefault("warn_detail", {})[name] = output_missing_reason
# ── 2026-08-12 系统 crontab 违规任务(老莫要求:显著提示。开发原则:自动任务统一 hermes,禁止系统 crontab)──
_sys_tasks = load_system_crontab_tasks()
for _t in _sys_tasks:
_t["layer"] = _pipeline_layer(_t.get("script", ""), _t.get("name", ""))
_t["output_missing"] = False
_t["output_missing_reason"] = ""
pipelines.append(_t)
if _sys_tasks:
feature_tree.setdefault("warn_detail", {})["system_crontab"] = (
f"发现 {len(_sys_tasks)} 个系统 crontab 部署的 MoFin 任务(违规:应迁移到 hermes)")
# ── 自检体系状态(L1功能健康/L3修复记录/L4元监控/L2卫生)──
self_check = {}
LOGS = Path('/home/hmo/MoFin/gateway/logs')
+18 -3
View File
@@ -347,7 +347,16 @@ function renderPipelineTable(pipelines) {
['monitor', '🩺 监控运维层', '健康检查/审计/备份/推送/自修复'],
];
const selStyle = 'background:#21262d;color:#c9d1d9;border:1px solid #30363d;border-radius:4px;padding:5px 8px;font-size:12px';
let html = '<div style="display:flex;gap:8px;margin-bottom:8px;align-items:center">';
// 2026-08-12 系统 crontab 违规统计(老莫:显著提示)
const sysCnt = pipelines.filter(p => p.source === 'system_crontab').length;
let html = '';
// 显著横幅:开发原则——禁止系统 crontab 部署自动任务,统一 hermes
html += `<div style="background:#3d1f1f;border:1px solid #f85149;border-left:4px solid #f85149;border-radius:6px;padding:10px 14px;margin-bottom:12px">` +
`<div style="font-weight:bold;color:#f85149;font-size:14px">🚫 开发原则(强制):自动任务必须部署在 hermesposition-analyst),严禁使用系统 crontab</div>` +
`<div style="color:#d29922;font-size:12px;margin-top:4px">来源标记:✅ hermes(合规)| ⚠️ 系统cron(违规,需迁移)。当前检测到 <strong>${sysCnt}</strong> 个系统 crontab 部署的 MoFin 任务。</div>` +
(sysCnt > 0 ? `<div style="color:#f85149;font-size:12px;margin-top:2px">⚠️ 存在违规系统 cron,请迁移到 hermes 后从系统 crontab 移除(统一体系,避免新旧一起跑)。</div>` : `<div style="color:#3fb950;font-size:12px;margin-top:2px">✅ 无违规系统 cron,体系统一。</div>`) +
`</div>`;
html += '<div style="display:flex;gap:8px;margin-bottom:8px;align-items:center">';
html += '<input class="search-box" id="pipeSearch" placeholder="搜索流程..." oninput="filterPipes()" style="flex:1">';
html += `<select id="pipeLayer" onchange="filterPipes()" style="${selStyle}">`;
html += '<option value="all">全部层</option><option value="collect">📥 采集层</option><option value="process">⚙️ 加工层</option><option value="use">📊 使用层</option><option value="monitor">🩺 监控层</option></select>';
@@ -367,10 +376,16 @@ function renderPipelineTable(pipelines) {
const tagCls = p.status==='ok'?'ok':p.status==='error'?'error':'warn';
const statusDisplay = p.last_run ? p.status : '待首次运行';
const profileBadge = p.profile==='position-analyst' ? '📋' : '📦';
// 2026-08-12 cron来源标记:系统 crontab 违规用红色警告(老莫要求显著提示)
const isSys = p.source === 'system_crontab';
const srcBadge = isSys
? '<span style="color:#f85149;font-weight:bold">⚠️ 系统cron(违规)</span>'
: '<span style="color:#3fb950">✅ hermes</span>';
const rowStyle = isSys ? ' style="background:#2d1515;border-left:3px solid #f85149"' : '';
const encName = encodeURIComponent(p.name || '');
const encScript = encodeURIComponent(p.script || '');
html += `<tr class="pipe-row" data-profile="${p.profile||'default'}" data-layer="${p.layer||'use'}" data-name="${(p.name||'').toLowerCase()}"><td>${p.name||p.script||'LLM'}</td>`;
html += `<td style="font-size:11px">${profileBadge} ${p.profile||'?'}</td>`;
html += `<tr class="pipe-row" data-profile="${p.profile||'default'}" data-layer="${p.layer||'use'}" data-name="${(p.name||'').toLowerCase()}"${rowStyle}><td>${p.name||p.script||'LLM'}</td>`;
html += `<td style="font-size:11px">${srcBadge} <span style="color:#8b949e">${profileBadge}${p.profile||'?'}</span></td>`;
html += `<td style="font-size:11px">${p.script||'LLM'}</td>`;
html += `<td><span class="cron-tag ${tagCls}">${p.type||'cron'}</span></td>`;
html += `<td style="font-size:11px">${p.schedule||'-'}</td>`;