Change the log time to China
Build and Push CodeReview / build (push) Waiting to run
Details
Build and Push CodeReview / build (push) Waiting to run
Details
This commit is contained in:
parent
5166027e2f
commit
3ce3767938
|
|
@ -0,0 +1,21 @@
|
||||||
|
from datetime import datetime, timedelta, timezone
|
||||||
|
|
||||||
|
# 中国标准时间 (UTC+8)
|
||||||
|
CHINA_TZ = timezone(timedelta(hours=8))
|
||||||
|
|
||||||
|
def get_now():
|
||||||
|
"""获取当前的中国时间 (UTC+8)"""
|
||||||
|
return datetime.now(CHINA_TZ)
|
||||||
|
|
||||||
|
def get_now_iso():
|
||||||
|
"""获取当前中国时间的 ISO 格式字符串"""
|
||||||
|
return get_now().isoformat()
|
||||||
|
|
||||||
|
def beijing_time(*args):
|
||||||
|
"""
|
||||||
|
用于 logging.Formatter.converter 的转换函数
|
||||||
|
"""
|
||||||
|
if args:
|
||||||
|
# args[0] 是 timestamp
|
||||||
|
return datetime.fromtimestamp(args[0], CHINA_TZ).timetuple()
|
||||||
|
return get_now().timetuple()
|
||||||
|
|
@ -8,9 +8,12 @@ from app.api.v1.api import api_router
|
||||||
from app.db.session import AsyncSessionLocal
|
from app.db.session import AsyncSessionLocal
|
||||||
from app.db.init_db import init_db
|
from app.db.init_db import init_db
|
||||||
|
|
||||||
|
from app.core.timezone import beijing_time
|
||||||
|
|
||||||
# 配置日志
|
# 配置日志
|
||||||
|
logging.Formatter.converter = beijing_time
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO,
|
level=logging.DEBUG,
|
||||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||||
datefmt='%Y-%m-%d %H:%M:%S'
|
datefmt='%Y-%m-%d %H:%M:%S'
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import asyncio
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional, Dict, Any, List, AsyncGenerator, Callable
|
from typing import Optional, Dict, Any, List, AsyncGenerator, Callable
|
||||||
from datetime import datetime, timezone
|
from app.core.timezone import get_now, get_now_iso
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
|
@ -281,7 +281,7 @@ class EventManager:
|
||||||
):
|
):
|
||||||
"""添加事件"""
|
"""添加事件"""
|
||||||
event_id = str(uuid.uuid4())
|
event_id = str(uuid.uuid4())
|
||||||
timestamp = datetime.now(timezone.utc)
|
timestamp = get_now()
|
||||||
|
|
||||||
event_data = {
|
event_data = {
|
||||||
"id": event_id,
|
"id": event_id,
|
||||||
|
|
@ -379,6 +379,7 @@ class EventManager:
|
||||||
finding_id=event_data["finding_id"],
|
finding_id=event_data["finding_id"],
|
||||||
tokens_used=event_data["tokens_used"],
|
tokens_used=event_data["tokens_used"],
|
||||||
event_metadata=sanitize_dict(event_data["metadata"]), # 🔥 清理元数据
|
event_metadata=sanitize_dict(event_data["metadata"]), # 🔥 清理元数据
|
||||||
|
created_at=get_now(),
|
||||||
)
|
)
|
||||||
db.add(event)
|
db.add(event)
|
||||||
await db.commit()
|
await db.commit()
|
||||||
|
|
@ -523,7 +524,7 @@ class EventManager:
|
||||||
|
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
# 发送心跳
|
# 发送心跳
|
||||||
yield {"event_type": "heartbeat", "timestamp": datetime.now(timezone.utc).isoformat()}
|
yield {"event_type": "heartbeat", "timestamp": get_now_iso()}
|
||||||
|
|
||||||
except GeneratorExit:
|
except GeneratorExit:
|
||||||
# SSE 连接断开
|
# SSE 连接断开
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import logging
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any, Dict, Optional, AsyncGenerator, List
|
from typing import Any, Dict, Optional, AsyncGenerator, List
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from datetime import datetime, timezone
|
from app.core.timezone import get_now_iso
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@ class StreamEvent:
|
||||||
"""流式事件"""
|
"""流式事件"""
|
||||||
event_type: StreamEventType
|
event_type: StreamEventType
|
||||||
data: Dict[str, Any] = field(default_factory=dict)
|
data: Dict[str, Any] = field(default_factory=dict)
|
||||||
timestamp: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
|
timestamp: str = field(default_factory=get_now_iso)
|
||||||
sequence: int = 0
|
sequence: int = 0
|
||||||
|
|
||||||
# 可选字段
|
# 可选字段
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ Tracer - 审计追踪器
|
||||||
import csv
|
import csv
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timezone
|
from app.core.timezone import get_now, get_now_iso
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Callable, Dict, List, Optional
|
from typing import Any, Callable, Dict, List, Optional
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
@ -53,7 +53,7 @@ class Tracer:
|
||||||
# 运行标识
|
# 运行标识
|
||||||
self.run_name = run_name
|
self.run_name = run_name
|
||||||
self.run_id = run_name or f"run-{uuid4().hex[:8]}"
|
self.run_id = run_name or f"run-{uuid4().hex[:8]}"
|
||||||
self.start_time = datetime.now(timezone.utc).isoformat()
|
self.start_time = get_now_iso()
|
||||||
self.end_time: Optional[str] = None
|
self.end_time: Optional[str] = None
|
||||||
|
|
||||||
# 追踪数据
|
# 追踪数据
|
||||||
|
|
@ -148,8 +148,8 @@ class Tracer:
|
||||||
"type": agent_type,
|
"type": agent_type,
|
||||||
"status": "running",
|
"status": "running",
|
||||||
"parent_id": parent_id,
|
"parent_id": parent_id,
|
||||||
"created_at": datetime.now(timezone.utc).isoformat(),
|
"created_at": get_now_iso(),
|
||||||
"updated_at": datetime.now(timezone.utc).isoformat(),
|
"updated_at": get_now_iso(),
|
||||||
"tool_executions": [],
|
"tool_executions": [],
|
||||||
"findings_count": 0,
|
"findings_count": 0,
|
||||||
}
|
}
|
||||||
|
|
@ -166,13 +166,13 @@ class Tracer:
|
||||||
"""更新 Agent 状态"""
|
"""更新 Agent 状态"""
|
||||||
if agent_id in self.agents:
|
if agent_id in self.agents:
|
||||||
self.agents[agent_id]["status"] = status
|
self.agents[agent_id]["status"] = status
|
||||||
self.agents[agent_id]["updated_at"] = datetime.now(timezone.utc).isoformat()
|
self.agents[agent_id]["updated_at"] = get_now_iso()
|
||||||
|
|
||||||
if error_message:
|
if error_message:
|
||||||
self.agents[agent_id]["error_message"] = error_message
|
self.agents[agent_id]["error_message"] = error_message
|
||||||
|
|
||||||
if status in ["completed", "failed", "stopped"]:
|
if status in ["completed", "failed", "stopped"]:
|
||||||
self.agents[agent_id]["finished_at"] = datetime.now(timezone.utc).isoformat()
|
self.agents[agent_id]["finished_at"] = get_now_iso()
|
||||||
|
|
||||||
# 触发回调
|
# 触发回调
|
||||||
if self.agent_status_callback:
|
if self.agent_status_callback:
|
||||||
|
|
@ -193,7 +193,7 @@ class Tracer:
|
||||||
execution_id = self._next_execution_id
|
execution_id = self._next_execution_id
|
||||||
self._next_execution_id += 1
|
self._next_execution_id += 1
|
||||||
|
|
||||||
now = datetime.now(timezone.utc).isoformat()
|
now = get_now_iso()
|
||||||
|
|
||||||
# 清理过大的参数
|
# 清理过大的参数
|
||||||
cleaned_args = self._clean_args(args)
|
cleaned_args = self._clean_args(args)
|
||||||
|
|
@ -226,7 +226,7 @@ class Tracer:
|
||||||
"""更新工具执行状态"""
|
"""更新工具执行状态"""
|
||||||
if execution_id in self.tool_executions:
|
if execution_id in self.tool_executions:
|
||||||
self.tool_executions[execution_id]["status"] = status
|
self.tool_executions[execution_id]["status"] = status
|
||||||
self.tool_executions[execution_id]["completed_at"] = datetime.now(timezone.utc).isoformat()
|
self.tool_executions[execution_id]["completed_at"] = get_now_iso()
|
||||||
|
|
||||||
# 清理过大的结果
|
# 清理过大的结果
|
||||||
if result is not None:
|
if result is not None:
|
||||||
|
|
@ -295,7 +295,7 @@ class Tracer:
|
||||||
"content": content,
|
"content": content,
|
||||||
"role": role,
|
"role": role,
|
||||||
"agent_id": agent_id,
|
"agent_id": agent_id,
|
||||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
"timestamp": get_now_iso(),
|
||||||
"metadata": metadata or {},
|
"metadata": metadata or {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -324,7 +324,7 @@ class Tracer:
|
||||||
"vulnerability_type": vulnerability_type,
|
"vulnerability_type": vulnerability_type,
|
||||||
"file_path": file_path,
|
"file_path": file_path,
|
||||||
"agent_id": agent_id,
|
"agent_id": agent_id,
|
||||||
"timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC"),
|
"timestamp": get_now().strftime("%Y-%m-%d %H:%M:%S") + " CST",
|
||||||
}
|
}
|
||||||
|
|
||||||
self.vulnerability_reports.append(report)
|
self.vulnerability_reports.append(report)
|
||||||
|
|
@ -365,12 +365,12 @@ class Tracer:
|
||||||
"scan_completed": True,
|
"scan_completed": True,
|
||||||
"content": content,
|
"content": content,
|
||||||
"success": success,
|
"success": success,
|
||||||
"completed_at": datetime.now(timezone.utc).isoformat(),
|
"completed_at": get_now_iso(),
|
||||||
"total_vulnerabilities": len(self.vulnerability_reports),
|
"total_vulnerabilities": len(self.vulnerability_reports),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.run_metadata["status"] = "completed" if success else "failed"
|
self.run_metadata["status"] = "completed" if success else "failed"
|
||||||
self.end_time = datetime.now(timezone.utc).isoformat()
|
self.end_time = get_now_iso()
|
||||||
self.run_metadata["end_time"] = self.end_time
|
self.run_metadata["end_time"] = self.end_time
|
||||||
|
|
||||||
logger.info(f"Tracer: Final scan result set, success={success}")
|
logger.info(f"Tracer: Final scan result set, success={success}")
|
||||||
|
|
@ -386,7 +386,7 @@ class Tracer:
|
||||||
run_dir = self.get_run_dir()
|
run_dir = self.get_run_dir()
|
||||||
|
|
||||||
if mark_complete:
|
if mark_complete:
|
||||||
self.end_time = datetime.now(timezone.utc).isoformat()
|
self.end_time = get_now_iso()
|
||||||
self.run_metadata["end_time"] = self.end_time
|
self.run_metadata["end_time"] = self.end_time
|
||||||
|
|
||||||
# 保存最终报告
|
# 保存最终报告
|
||||||
|
|
@ -410,7 +410,7 @@ class Tracer:
|
||||||
|
|
||||||
with report_file.open("w", encoding="utf-8") as f:
|
with report_file.open("w", encoding="utf-8") as f:
|
||||||
f.write("# 安全审计报告\n\n")
|
f.write("# 安全审计报告\n\n")
|
||||||
f.write(f"**生成时间:** {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC')}\n")
|
f.write(f"**生成时间:** {get_now().strftime('%Y-%m-%d %H:%M:%S')} CST\n")
|
||||||
f.write(f"**运行ID:** {self.run_id}\n\n")
|
f.write(f"**运行ID:** {self.run_id}\n\n")
|
||||||
|
|
||||||
# 统计信息
|
# 统计信息
|
||||||
|
|
@ -518,11 +518,11 @@ class Tracer:
|
||||||
def _calculate_duration(self) -> float:
|
def _calculate_duration(self) -> float:
|
||||||
"""计算运行时长"""
|
"""计算运行时长"""
|
||||||
try:
|
try:
|
||||||
start = datetime.fromisoformat(self.start_time.replace("Z", "+00:00"))
|
start = datetime.fromisoformat(self.start_time)
|
||||||
if self.end_time:
|
if self.end_time:
|
||||||
end = datetime.fromisoformat(self.end_time.replace("Z", "+00:00"))
|
end = datetime.fromisoformat(self.end_time)
|
||||||
else:
|
else:
|
||||||
end = datetime.now(timezone.utc)
|
end = get_now()
|
||||||
return (end - start).total_seconds()
|
return (end - start).total_seconds()
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ PDF 报告生成服务 - 专业审计版 (WeasyPrint)
|
||||||
import io
|
import io
|
||||||
import html
|
import html
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from app.core.timezone import get_now
|
||||||
from typing import List, Dict, Any
|
from typing import List, Dict, Any
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
|
|
@ -475,8 +476,8 @@ class ReportGenerator:
|
||||||
context = {
|
context = {
|
||||||
'title': '代码审计报告',
|
'title': '代码审计报告',
|
||||||
'subtitle': f'即时分析 | 语言: {language.capitalize()}',
|
'subtitle': f'即时分析 | 语言: {language.capitalize()}',
|
||||||
'generated_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
'generated_at': get_now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
'report_id': f"INST-{int(datetime.now().timestamp())}",
|
'report_id': f"INST-{int(get_now().timestamp())}",
|
||||||
'score': score,
|
'score': score,
|
||||||
'stats': [
|
'stats': [
|
||||||
('问题总数', len(issues)),
|
('问题总数', len(issues)),
|
||||||
|
|
@ -493,7 +494,7 @@ class ReportGenerator:
|
||||||
context = {
|
context = {
|
||||||
'title': '项目代码审计报告',
|
'title': '项目代码审计报告',
|
||||||
'subtitle': f"项目: {project} | 分支: {task.get('branch_name', 'default')}",
|
'subtitle': f"项目: {project} | 分支: {task.get('branch_name', 'default')}",
|
||||||
'generated_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
'generated_at': get_now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
'report_id': f"TASK-{task.get('id', '')[:8]}",
|
'report_id': f"TASK-{task.get('id', '')[:8]}",
|
||||||
'score': score,
|
'score': score,
|
||||||
'stats': [
|
'stats': [
|
||||||
|
|
|
||||||
|
|
@ -59,10 +59,7 @@ function agentAuditReducer(state: AgentAuditState, action: AgentAuditAction): Ag
|
||||||
return { ...state, logs: action.payload };
|
return { ...state, logs: action.payload };
|
||||||
|
|
||||||
case 'ADD_LOG': {
|
case 'ADD_LOG': {
|
||||||
const { id: providedId, ...logData } = action.payload;
|
const newLog = createLogItem(action.payload);
|
||||||
const newLog = providedId
|
|
||||||
? { ...createLogItem(logData), id: providedId }
|
|
||||||
: createLogItem(logData);
|
|
||||||
return { ...state, logs: [...state.logs, newLog] };
|
return { ...state, logs: [...state.logs, newLog] };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,11 +191,11 @@ export function useAgentAuditState() {
|
||||||
dispatch({ type: 'SET_AGENT_TREE', payload: tree });
|
dispatch({ type: 'SET_AGENT_TREE', payload: tree });
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addLog = useCallback((log: Omit<LogItem, 'id' | 'time'>): string => {
|
const addLog = useCallback((log: Omit<LogItem, 'id' | 'time'> & { id?: string; time?: string }): string => {
|
||||||
const newLog = createLogItem(log);
|
const newLog = createLogItem(log);
|
||||||
dispatch({ type: 'SET_LOGS', payload: [...state.logs, newLog] });
|
dispatch({ type: 'ADD_LOG', payload: newLog });
|
||||||
return newLog.id;
|
return newLog.id;
|
||||||
}, [state.logs]);
|
}, []);
|
||||||
|
|
||||||
const updateLog = useCallback((id: string, updates: Partial<LogItem>) => {
|
const updateLog = useCallback((id: string, updates: Partial<LogItem>) => {
|
||||||
dispatch({ type: 'UPDATE_LOG', payload: { id, updates } });
|
dispatch({ type: 'UPDATE_LOG', payload: { id, updates } });
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,7 @@ function AgentAuditPageContent() {
|
||||||
title: event.message?.slice(0, 100) + (event.message && event.message.length > 100 ? '...' : '') || 'Thinking...',
|
title: event.message?.slice(0, 100) + (event.message && event.message.length > 100 ? '...' : '') || 'Thinking...',
|
||||||
content: event.message || (event.metadata?.thought as string) || '',
|
content: event.message || (event.metadata?.thought as string) || '',
|
||||||
agentName,
|
agentName,
|
||||||
|
time: event.timestamp,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
processedCount++;
|
processedCount++;
|
||||||
|
|
@ -217,6 +218,7 @@ function AgentAuditPageContent() {
|
||||||
status: 'running' as const,
|
status: 'running' as const,
|
||||||
},
|
},
|
||||||
agentName,
|
agentName,
|
||||||
|
time: event.timestamp,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
processedCount++;
|
processedCount++;
|
||||||
|
|
@ -237,6 +239,7 @@ function AgentAuditPageContent() {
|
||||||
status: 'completed' as const,
|
status: 'completed' as const,
|
||||||
},
|
},
|
||||||
agentName,
|
agentName,
|
||||||
|
time: event.timestamp,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
processedCount++;
|
processedCount++;
|
||||||
|
|
@ -253,6 +256,7 @@ function AgentAuditPageContent() {
|
||||||
title: event.message || (event.metadata?.title as string) || 'Vulnerability found',
|
title: event.message || (event.metadata?.title as string) || 'Vulnerability found',
|
||||||
severity: (event.metadata?.severity as string) || 'medium',
|
severity: (event.metadata?.severity as string) || 'medium',
|
||||||
agentName,
|
agentName,
|
||||||
|
time: event.timestamp,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
processedCount++;
|
processedCount++;
|
||||||
|
|
@ -271,6 +275,7 @@ function AgentAuditPageContent() {
|
||||||
type: 'dispatch',
|
type: 'dispatch',
|
||||||
title: event.message || `Event: ${event.event_type}`,
|
title: event.message || `Event: ${event.event_type}`,
|
||||||
agentName,
|
agentName,
|
||||||
|
time: event.timestamp,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
processedCount++;
|
processedCount++;
|
||||||
|
|
@ -284,6 +289,7 @@ function AgentAuditPageContent() {
|
||||||
type: 'info',
|
type: 'info',
|
||||||
title: event.message || 'Task completed',
|
title: event.message || 'Task completed',
|
||||||
agentName,
|
agentName,
|
||||||
|
time: event.timestamp,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
processedCount++;
|
processedCount++;
|
||||||
|
|
@ -297,6 +303,7 @@ function AgentAuditPageContent() {
|
||||||
type: 'error',
|
type: 'error',
|
||||||
title: event.message || 'Task error',
|
title: event.message || 'Task error',
|
||||||
agentName,
|
agentName,
|
||||||
|
time: event.timestamp,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
processedCount++;
|
processedCount++;
|
||||||
|
|
@ -310,6 +317,7 @@ function AgentAuditPageContent() {
|
||||||
type: 'info',
|
type: 'info',
|
||||||
title: event.message || 'Task cancelled',
|
title: event.message || 'Task cancelled',
|
||||||
agentName,
|
agentName,
|
||||||
|
time: event.timestamp,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
processedCount++;
|
processedCount++;
|
||||||
|
|
@ -335,6 +343,7 @@ function AgentAuditPageContent() {
|
||||||
progressKey: matchedProgress.key,
|
progressKey: matchedProgress.key,
|
||||||
title: event.message,
|
title: event.message,
|
||||||
agentName,
|
agentName,
|
||||||
|
time: event.timestamp,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -344,6 +353,7 @@ function AgentAuditPageContent() {
|
||||||
type: 'info',
|
type: 'info',
|
||||||
title: event.message,
|
title: event.message,
|
||||||
agentName,
|
agentName,
|
||||||
|
time: event.timestamp,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -374,6 +384,7 @@ function AgentAuditPageContent() {
|
||||||
progressKey: matchedProgress.key,
|
progressKey: matchedProgress.key,
|
||||||
title: message,
|
title: message,
|
||||||
agentName,
|
agentName,
|
||||||
|
time: event.timestamp,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -383,6 +394,7 @@ function AgentAuditPageContent() {
|
||||||
type: event.event_type === 'error' ? 'error' : 'info',
|
type: event.event_type === 'error' ? 'error' : 'info',
|
||||||
title: message,
|
title: message,
|
||||||
agentName,
|
agentName,
|
||||||
|
time: event.timestamp,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -406,6 +418,7 @@ function AgentAuditPageContent() {
|
||||||
type: 'info',
|
type: 'info',
|
||||||
title: event.message,
|
title: event.message,
|
||||||
agentName,
|
agentName,
|
||||||
|
time: event.timestamp,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
processedCount++;
|
processedCount++;
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ export type AgentAuditAction =
|
||||||
| { type: 'ADD_FINDING'; payload: Partial<AgentFinding> & { id: string } }
|
| { type: 'ADD_FINDING'; payload: Partial<AgentFinding> & { id: string } }
|
||||||
| { type: 'SET_AGENT_TREE'; payload: AgentTreeResponse }
|
| { type: 'SET_AGENT_TREE'; payload: AgentTreeResponse }
|
||||||
| { type: 'SET_LOGS'; payload: LogItem[] }
|
| { type: 'SET_LOGS'; payload: LogItem[] }
|
||||||
| { type: 'ADD_LOG'; payload: Omit<LogItem, 'id' | 'time'> & { id?: string } }
|
| { type: 'ADD_LOG'; payload: Omit<LogItem, 'id' | 'time'> & { id?: string; time?: string } }
|
||||||
| { type: 'UPDATE_LOG'; payload: { id: string; updates: Partial<LogItem> } }
|
| { type: 'UPDATE_LOG'; payload: { id: string; updates: Partial<LogItem> } }
|
||||||
| { type: 'UPDATE_OR_ADD_PROGRESS_LOG'; payload: { progressKey: string; title: string; agentName?: string } }
|
| { type: 'UPDATE_OR_ADD_PROGRESS_LOG'; payload: { progressKey: string; title: string; agentName?: string } }
|
||||||
| { type: 'COMPLETE_TOOL_LOG'; payload: { toolName: string; output: string; duration: number } }
|
| { type: 'COMPLETE_TOOL_LOG'; payload: { toolName: string; output: string; duration: number } }
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,18 @@ export function resetLogIdCounter(): void {
|
||||||
* Get current time string for logs
|
* Get current time string for logs
|
||||||
*/
|
*/
|
||||||
export function getTimeString(): string {
|
export function getTimeString(): string {
|
||||||
return new Date().toLocaleTimeString('en-US', {
|
return formatTimestamp(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format timestamp (date or string) to HH:mm:ss
|
||||||
|
*/
|
||||||
|
export function formatTimestamp(time?: string | Date | number): string {
|
||||||
|
if (!time) return getTimeString();
|
||||||
|
const date = new Date(time);
|
||||||
|
if (isNaN(date.getTime())) return getTimeString();
|
||||||
|
|
||||||
|
return date.toLocaleTimeString('en-US', {
|
||||||
hour12: false,
|
hour12: false,
|
||||||
hour: '2-digit',
|
hour: '2-digit',
|
||||||
minute: '2-digit',
|
minute: '2-digit',
|
||||||
|
|
@ -84,12 +95,12 @@ export function getTimeString(): string {
|
||||||
/**
|
/**
|
||||||
* Create a log item
|
* Create a log item
|
||||||
*/
|
*/
|
||||||
export function createLogItem(item: Omit<LogItem, 'id' | 'time'>): LogItem {
|
export function createLogItem(item: Omit<LogItem, 'id' | 'time'> & { id?: string; time?: string }): LogItem {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
id: generateLogId(),
|
id: item.id || generateLogId(),
|
||||||
time: getTimeString(),
|
time: item.time ? formatTimestamp(item.time) : getTimeString(),
|
||||||
};
|
} as LogItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue