| 102 | |
| 103 | @dataclass |
| 104 | class ReconScanState: |
| 105 | scan_id: str |
| 106 | target: str |
| 107 | recon_types: List[ReconType] |
| 108 | status: str = "pending" |
| 109 | started_at: Optional[datetime] = None |
| 110 | completed_at: Optional[datetime] = None |
| 111 | results: Dict[ReconType, ReconResult] = field(default_factory=dict) |
| 112 | handed_off: bool = False |
| 113 | handoff_zap_scan_id: Optional[str] = None |
| 114 | error_message: str = "" |
| 115 | |
| 116 | def to_dict(self) -> Dict[str, Any]: |
| 117 | return { |
| 118 | "scan_id": self.scan_id, |
| 119 | "target": self.target, |
| 120 | "recon_types": [r.value for r in self.recon_types], |
| 121 | "status": self.status, |
| 122 | "started_at": self.started_at.isoformat() if self.started_at else None, |
| 123 | "completed_at": self.completed_at.isoformat() if self.completed_at else None, |
| 124 | "results": {r.value: result.to_dict() for r, result in self.results.items()}, |
| 125 | "handed_off": self.handed_off, |
| 126 | "handoff_zap_scan_id": self.handoff_zap_scan_id, |
| 127 | "error_message": self.error_message, |
| 128 | "duration_seconds": ( |
| 129 | (self.completed_at or datetime.now()) - self.started_at |
| 130 | ).total_seconds() if self.started_at else 0, |
| 131 | } |
| 132 | |
| 133 | |
| 134 | class ReconEngine: |
no outgoing calls