(task_dir: Path)
| 104 | |
| 105 | |
| 106 | def build_steps(task_dir: Path) -> tuple[list[dict], str]: |
| 107 | log_steps, final = parse_log(task_dir / "final_script_log.txt") |
| 108 | raw_rows = parse_steps_jsonl(task_dir / "steps.jsonl") |
| 109 | |
| 110 | out: list[dict] = [] |
| 111 | for row in raw_rows: |
| 112 | n = row["step_num"] |
| 113 | action_text = log_steps.get(n) or row.get("action", "").strip() |
| 114 | # the first row of steps.jsonl sometimes contains the full multi-line |
| 115 | # log dump. If so, take only its first "step N action:" line. |
| 116 | if action_text and "\nstep " in action_text.lower(): |
| 117 | first_line = action_text.splitlines()[0] |
| 118 | m = _STEP_LINE.match(first_line) |
| 119 | if m: |
| 120 | action_text = m.group(2).strip() |
| 121 | shot = row.get("screenshot") or "" |
| 122 | if shot: |
| 123 | shot = Path(shot).name |
| 124 | urls = [_clean_url(u) for u in _URL_RE.findall(action_text)] |
| 125 | seen: set[str] = set() |
| 126 | urls = [u for u in urls if not (u in seen or seen.add(u))] |
| 127 | out.append({ |
| 128 | "step_num": n, |
| 129 | "action": action_text, |
| 130 | "screenshot": shot, |
| 131 | "urls": urls, |
| 132 | }) |
| 133 | return out, final |
| 134 | |
| 135 | |
| 136 | def _host(u: str) -> str: |
no test coverage detected