Files
MoFin/scripts/analyze_health.py
T
hmo 57377e9dd3 feat(auto_heal): multi-profile key switching + fix two error classes
Error investigation (from restored monitoring) found 2 root causes:

1. 'Blocked: script path resolves outside scripts dir' (5+ cron jobs):
   Jul 17 symlink refactor replaced real scripts with symlinks; the hermes
   cron scheduler's security check (Path.resolve + relative_to) rejects
   symlink escape. ALL no_agent script jobs blocked since Jul 17 23:12.
   FIX: converted 102 symlinks to hardlinks (same inode, resolve() stays
   inside scripts_dir, single-source still works). Permanent structural fix.

2. HTTP 429 on default profile (知识研究/梦境循环/wiki-self-growth/
   evolution-pulse/大脑任务执行): default gateway used ocg-key1 (weekly
   100%). FIX: switched default profile to ocg-key6 + added missing
   provider block. LLM verified working (3.1s).

auto_heal extended to actually cover these automatically next time:
- PROFILES registry: zhiwei (8643, system svc) + default (8642, user svc)
- current_provider/switch_key/_scan_agent_log parameterized by profile
- health() now reports llm_provider_default (agent.log scan)
- auto_heal: per-profile 429 detection -> best_key -> switch_key(profile)
- _ensure_provider_block: injects missing provider credentials from
  zhiwei config (single source of truth) into target config
2026-07-20 01:12:38 +08:00

45 lines
1.7 KiB
Python

import json
d = json.load(open('/tmp/mofin_health.json'))
print("generated_at:", d.get('generated_at'))
print()
# 1. Pipelines with error/warn status
print("=== PIPELINES (error/warn) ===")
for p in d.get('pipelines', []):
if p.get('status') in ('error', 'fail', 'warn'):
print(f"[{p['status']}] {p.get('name') or p.get('script')} | profile={p.get('profile')} | type={p.get('type')} | schedule={p.get('schedule')} | last_run={p.get('last_run')}")
print()
print("=== FEATURE TREE (non-ok nodes) ===")
def walk(n, path=''):
label = n.get('label', '?')
p = f"{path}/{label}"
if n.get('status') not in ('ok', None):
print(f"[{n.get('status')}] {p}")
for pipe in n.get('pipes', []):
if pipe.get('status') != 'ok':
print(f" pipe: [{pipe.get('status')}] {pipe.get('name') or pipe.get('script')} | last_run={pipe.get('last_run')}")
for c in n.get('children', []):
walk(c, p)
walk(d.get('feature_tree', {}))
print()
print("=== DATA ENTITIES (orphan/write_only) ===")
for e in d.get('entities', []):
if e.get('flow_status') in ('orphan', 'write_only'):
print(f"[{e['flow_status']}] {e['name']} | rows={e.get('rows')} | writers={e.get('writers')} | readers={e.get('readers')} | {e.get('desc','')[:60]}")
print()
print("=== ARCHITECTURE violations ===")
arch = d.get('architecture', {})
print("violation_count:", arch.get('violation_count'))
for v in (arch.get('price_api_violations') or [])[:10]:
print(f" {v.get('script')} L{v.get('line')}")
print()
print("=== JSON files with warn ===")
for j in d.get('json_files', []):
if j.get('warn'):
print(f"[warn] {j['name']} | {j.get('desc','')[:60]} | readers={j.get('readers')}")