feat(plugins): add source-aware evidence and recording timelines

This commit is contained in:
chenmingxuan committed 2026-10-16 10:20:11 +08:00
1 parent 72ff342605
commit ef7af9988e
61 files changed
+3847 -108

No files matched your search

+67 -4
View File
@@ -475,6 +475,24 @@ def plugin_operations(steps: list[dict]) -> list[dict]:
for block in result.get("content", [])
if isinstance(block, dict) and block.get("type") == "text"
)
source = result.get("source") or payload.get("source") or {}
evidence = []
if result.get("call_id"):
evidence.append("调用:" + str(result["call_id"]))
if source:
evidence.append(
f"来源:{source.get('kind', '')} · {source.get('label') or source.get('target_id', '')}"
)
if result.get("execution"):
evidence.append(
f"执行:{result['execution']};副作用:{result.get('side_effect', 'unknown')};错误码:{result.get('error_code') or '无'}"
)
for attachment in result.get("attachments") or []:
evidence.append(
f"文件:{attachment.get('name')};SHA-256:{attachment.get('sha256')}"
)
if evidence:
content = "\n".join([content or result.get("message") or "", *evidence])
operations.append(
{
"step": step.get("step_number"),
@@ -809,6 +827,8 @@ def session_artifacts(report: dict) -> list[tuple[Path, str]]:
def export_session_media(session_id: str, kind: str) -> Path:
from apps.admin_console.services.evidence_service import export_index, plugin_files
if kind not in {"screenshots", "video", "all"}:
raise ValueError("不支持的下载类型")
with EXPORT_LOCK:
@@ -818,7 +838,10 @@ def export_session_media(session_id: str, kind: str) -> Path:
images = session_screenshots(report) if kind in {"screenshots", "all"} else []
videos = session_videos(report) if kind in {"video", "all"} else []
artifacts = session_artifacts(report) if kind == "all" else []
if kind == "video" and not videos:
extras = plugin_files(session_id) if kind in {"video", "all"} else []
if kind == "video":
extras = [(path, name) for path, name in extras if path.suffix.lower() == ".mp4"]
if kind == "video" and not videos and not extras:
raise ValueError("此任务没有已保存的录屏")
if kind == "screenshots" and not images:
raise ValueError("此任务没有可下载的步骤截图")
@@ -830,7 +853,8 @@ def export_session_media(session_id: str, kind: str) -> Path:
"status": report["status"],
"verdict": report["verdict"],
"screenshots": [name for _, name in images],
"videos": [name for _, name in videos],
"videos": ["任务录屏/" + name for _, name in videos]
+ [name for path, name in extras if path.suffix.lower() == ".mp4"],
"artifacts": [name for _, name in artifacts],
"note": "录屏按时间顺序分段;暂停期间未占用设备,也未录制其他任务。",
}
@@ -842,9 +866,27 @@ def export_session_media(session_id: str, kind: str) -> Path:
for source, name in images:
archive.write(source, f"{prefix}步骤截图/{name}")
for source, name in videos:
archive.write(source, f"{prefix}任务录屏/{name}")
archive.write(source, f"{prefix}任务录屏/{name}", compress_type=zipfile.ZIP_STORED)
for source, name in artifacts:
archive.write(source, f"{prefix}任务文件/安卓/{name}")
for source, name in extras:
archive.write(
source,
prefix + name,
compress_type=zipfile.ZIP_STORED
if source.suffix.lower() == ".mp4"
else zipfile.ZIP_DEFLATED,
)
if kind == "all":
archive.writestr(
prefix + "证据索引.json",
json.dumps(
export_index(session_id, report, videos),
ensure_ascii=False,
indent=2,
default=str,
),
)
archive.writestr(
prefix + "文件清单.json", json.dumps(manifest, ensure_ascii=False, indent=2)
)
@@ -853,6 +895,8 @@ def export_session_media(session_id: str, kind: str) -> Path:
def export_batch(batch_id: str, progress: Callable[[int, int, str], None] | None = None) -> Path:
from apps.admin_console.services.evidence_service import export_index, plugin_files
with EXPORT_LOCK:
batch = store.get("batch", identifier(batch_id))
if not batch:
@@ -901,9 +945,28 @@ def export_batch(batch_id: str, progress: Callable[[int, int, str], None] | None
for source, name in session_screenshots(report):
archive.write(source, f"{prefix}/步骤截图/{name}")
for source, name in session_videos(report):
archive.write(source, f"{prefix}/任务录屏/{name}")
archive.write(
source, f"{prefix}/任务录屏/{name}", compress_type=zipfile.ZIP_STORED
)
for source, name in session_artifacts(report):
archive.write(source, f"{prefix}/任务文件/安卓/{name}")
for source, name in plugin_files(report["id"]):
archive.write(
source,
f"{prefix}/{name}",
compress_type=zipfile.ZIP_STORED
if source.suffix.lower() == ".mp4"
else zipfile.ZIP_DEFLATED,
)
archive.writestr(
f"{prefix}/证据索引.json",
json.dumps(
export_index(report["id"], report, session_videos(report)),
ensure_ascii=False,
indent=2,
default=str,
),
)
if progress:
progress(number, len(records), f"已打包任务 {number}/{len(records)}")
if progress: