|
|
@@ -332,8 +332,10 @@ def render(ctx, title, panels):
|
|
|
ctx['views'].append(view)
|
|
|
try:
|
|
|
vp = ctx['cfg']['out'] / 'views.json'
|
|
|
- old = json.load(open(vp)) if vp.exists() else []
|
|
|
- json.dump((old + [dict(id=view['id'], title=view['title'], panels=[dict(plugin=x['plugin'], args=x['args'], svg=x['svg'], text=x['text']) for x in out])])[-20:], open(vp, 'w'), ensure_ascii=False)
|
|
|
+ # 读写都要显式 utf-8: 面板文本是中文, 缺 encoding 会按系统 locale(cp936) 读/写 → 乱码或崩
|
|
|
+ old = json.load(open(vp, encoding='utf-8')) if vp.exists() else []
|
|
|
+ with open(vp, 'w', encoding='utf-8') as f:
|
|
|
+ json.dump((old + [dict(id=view['id'], title=view['title'], panels=[dict(plugin=x['plugin'], args=x['args'], svg=x['svg'], text=x['text']) for x in out])])[-20:], f, ensure_ascii=False)
|
|
|
except Exception:
|
|
|
pass
|
|
|
return dict(kind='view', data=view, text=f'视图 "{title}" 已组装 {len(out)} 个面板: ' + '; '.join(f'{x["plugin"]}: {x["text"][:120]}' for x in out))
|
|
|
@@ -349,7 +351,7 @@ def analyze(ctx, input='', window='', steps='', confirm=False):
|
|
|
lock = ctx['cfg']['out'] / 'analyze.lock'
|
|
|
if lock.exists():
|
|
|
try:
|
|
|
- pid = int(lock.read_text().strip())
|
|
|
+ pid = int(lock.read_text(encoding='utf-8').strip())
|
|
|
os.kill(pid, 0)
|
|
|
return dict(kind='text', text=f'analyze 已在运行 (pid {pid}), 不重复启动; 用 job_status 看进度.')
|
|
|
except Exception:
|
|
|
@@ -365,8 +367,11 @@ def analyze(ctx, input='', window='', steps='', confirm=False):
|
|
|
cmd += ['--steps'] + [s for s in str(steps).replace(',', ' ').split() if s]
|
|
|
logp = ctx['cfg']['out'] / 'analyze_stdout.log'
|
|
|
ctx['cfg']['out'].mkdir(parents=True, exist_ok=True)
|
|
|
- p = subprocess.Popen(cmd, cwd=str(ROOT), stdout=open(logp, 'w'), stderr=subprocess.STDOUT)
|
|
|
- lock.write_text(str(p.pid))
|
|
|
+ # stdout= 只用到这个文件的 fd (内容由子进程自己写), 所以这里不 with-close:
|
|
|
+ # 句柄留着, 免得父进程提前关掉; 显式 encoding 是为了不留"缺 encoding"的门禁告警。
|
|
|
+ _logf = open(logp, 'w', encoding='utf-8')
|
|
|
+ p = subprocess.Popen(cmd, cwd=str(ROOT), stdout=_logf, stderr=subprocess.STDOUT)
|
|
|
+ lock.write_text(str(p.pid), encoding='utf-8')
|
|
|
return dict(kind='text', data=dict(pid=p.pid, log=str(logp)), text=f'全链已在后台启动 (pid {p.pid}); 进度看 job_status; 日志 {logp}. 完成后产物/报告/知识库自动刷新 (界面需刷新页面).')
|
|
|
|
|
|
|
|
|
@@ -385,8 +390,9 @@ def farm_overview(ctx):
|
|
|
def job_status(ctx):
|
|
|
out = ctx['cfg']['out']
|
|
|
logp, lp = out / 'analyze_stdout.log', out / 'analyze_log.json'
|
|
|
- tail = open(logp).read()[-1200:] if logp.exists() else '(无运行日志)'
|
|
|
- last = json.load(open(lp)) if lp.exists() else None
|
|
|
+ # 日志与结果都是中文文本: 不写 encoding 会按系统 locale(cp936) 读 → 乱码/解码失败
|
|
|
+ tail = open(logp, encoding='utf-8').read()[-1200:] if logp.exists() else '(无运行日志)'
|
|
|
+ last = json.load(open(lp, encoding='utf-8')) if lp.exists() else None
|
|
|
summ = (f"上次结果: {last['status']} {last['seconds']}s @ {last['finished']}; 窗 {last['windows']}; 步骤 " + ', '.join(f"{s['step']}:{s['rc']}/{s['seconds']}s" for s in last['steps'])) if last else '尚无完成记录'
|
|
|
return dict(kind='text', data=last, text=f'{summ}\n--- 实时日志尾 ---\n{tail}')
|
|
|
|