feat: LLM-анализ — индикатор прогресса, кнопка рескана, статистика на дашборде
- Добавлен статус {"status": "analyzing"} в finding.report на время LLM-анализа
- Кнопка рескана (Retry) под LLM-отчётом в ручном режиме
- LLM-статистика на дашборде: analysed / pending
- Защита от двойного анализа через per-finding asyncio.Lock
- _llm_spinner.html — фрагмент спиннера для состояния analysing
- Удалён мёртвый код: constants, i18n, CSS, queries
- Фиксы: _env_int, индексы БД, UnicodeDecodeError, time.mktime и др.
- Шаблоны: shared includes (_status_badge, _pagination)
- AGENTS.md: workflow (lint, test, commit, rebuild)
This commit is contained in:
@@ -43,6 +43,9 @@ async def scan_package(filepath: str, ecosystem: str = DEFAULT_ECOSYSTEM) -> dic
|
||||
log.error("GuardDog exited %d: %s", proc.returncode, stderr.decode())
|
||||
return {"findings": [], "errors": [stderr.decode().strip()]}
|
||||
|
||||
if proc.returncode == 1 and stderr:
|
||||
log.warning("GuardDog stderr (exit 1): %s", stderr.decode().strip())
|
||||
|
||||
try:
|
||||
data = json.loads(stdout.decode())
|
||||
except json.JSONDecodeError:
|
||||
@@ -96,6 +99,17 @@ def _normalize_output(data: dict) -> dict:
|
||||
)
|
||||
elif isinstance(value, dict) and not value:
|
||||
continue
|
||||
elif isinstance(value, dict):
|
||||
# Non-empty dict — treat as a single finding
|
||||
findings.append(
|
||||
{
|
||||
"rule": rule_name,
|
||||
"severity": value.get("severity", DEFAULT_FINDING_SEVERITY),
|
||||
"message": value.get("message", ""),
|
||||
"location": value.get("location", ""),
|
||||
"code": value.get("code", ""),
|
||||
}
|
||||
)
|
||||
|
||||
errors = data.get("errors", {})
|
||||
if isinstance(errors, dict):
|
||||
|
||||
Reference in New Issue
Block a user