merge: cập nhật từ origin/feature/delta-team/epic-R04 (dọn dead code, gộp i18n/theme, CASAN Gate O, launcher)
Kéo 7 commit mới từ remote — chủ yếu dọn dẹp và siết chất lượng, không đổi API đang dùng: - Xoá 1.400 dòng mã chết + 5 gói rỗng còn sót sau các lần merge trước. - Thêm CASAN Gate O (LOC) áp cho toàn cây mã. - Sửa 5 checker UI hỏng sau đợt tách widget R08, vá 4 hồi quy. - install.bat/run.bat, gộp requirements-test.txt vào requirements.txt. - Gom i18n_*.py / theme_*.py rời rạc thành gói i18n/ và theme/. Merge sạch, không có conflict marker nào (git tự resolve toàn bộ). Đã kiểm tra lại 4 điểm đã vá ở 2 lần merge trước (config.py circular import, ai_edit_model_resolver.py dùng resolve() thay route_turn(), _confirm_routing_switch nhận timeout, RoutingApplicationService.resolve()) — cả 4 vẫn nguyên vẹn sau merge này. pytest tests/: 793 passed — giống hệt số liệu trước khi merge, không phát sinh fail/error mới (8 fail còn lại vẫn là do môi trường sandbox: thiếu keyring, tên thư mục cowork-local vs cowork_local). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,9 @@ class DashboardQueryService:
|
||||
"""
|
||||
|
||||
def __init__(self, ctx: Any, directory: Optional[Path] = None) -> None:
|
||||
"""``directory`` để None thì đọc thư mục telemetry mặc định; test trỏ nó vào
|
||||
``tmp_path`` để không chạm dữ liệu thật.
|
||||
"""
|
||||
self.ctx = ctx
|
||||
self._directory = directory
|
||||
|
||||
@@ -94,16 +97,21 @@ class DashboardQueryService:
|
||||
return ut.period_totals(events, granularity, self.pricing(), offset)
|
||||
|
||||
def period_range_label(self, granularity: str, offset: int) -> str:
|
||||
"""Nhãn hiển thị của một kỳ (tuần/tháng/năm cộng độ lệch)."""
|
||||
from cowork_local.core import usage_tracker as ut
|
||||
|
||||
return ut.period_range_label(granularity, offset)
|
||||
|
||||
def budget_status(self):
|
||||
"""Tình trạng ngân sách: đã dùng bao nhiêu, còn lại bao nhiêu, có vượt ngưỡng chưa."""
|
||||
from cowork_local.core import usage_tracker as ut
|
||||
|
||||
return ut.budget_status(self.ctx.config)
|
||||
|
||||
def set_budget(self, amount: float, currency: str) -> None:
|
||||
"""Đặt hạn mức ngân sách mới — mở một chu kỳ đếm mới, chi tiêu trước đó không
|
||||
còn được tính vào.
|
||||
"""
|
||||
from cowork_local.core import usage_tracker as ut
|
||||
|
||||
ut.set_budget(self.ctx.config, amount, currency)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
"""DTO của phân hệ Giám sát: hình dạng dữ liệu mà tầng application trả cho
|
||||
giao diện, không phụ thuộc nguồn đọc.
|
||||
"""
|
||||
|
||||
@@ -12,6 +12,9 @@ from typing import Any, Dict
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AuditEventDTO:
|
||||
"""Một sự kiện kiểm toán ở dạng tầng application dùng — không phụ thuộc khuôn
|
||||
lưu trên đĩa, nên đổi định dạng nhật ký không kéo theo sửa giao diện.
|
||||
"""
|
||||
ts: str
|
||||
kind: str
|
||||
name: str
|
||||
@@ -40,6 +43,7 @@ class AuditEventDTO:
|
||||
)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Bản ghi dưới dạng dict cho lớp giao diện."""
|
||||
return {
|
||||
"ts": self.ts, "kind": self.kind, "agent_role": self.agent_role,
|
||||
"name": self.name, "ok": self.ok, "detail": self.detail,
|
||||
|
||||
@@ -16,6 +16,7 @@ from .repository.audit_event_repository import AuditEventRepository
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Page:
|
||||
"""Một trang kết quả truy vấn nhật ký: các mục, tổng số, số trang và cỡ trang."""
|
||||
items: List[AuditEventDTO]
|
||||
total: int
|
||||
page: int
|
||||
@@ -23,6 +24,7 @@ class Page:
|
||||
|
||||
@property
|
||||
def has_more(self) -> bool:
|
||||
"""Còn trang sau nữa không."""
|
||||
return self.page * self.page_size < self.total
|
||||
|
||||
|
||||
@@ -31,11 +33,15 @@ class MonitoringQueryService:
|
||||
audit log; this service never writes anything."""
|
||||
|
||||
def __init__(self, repository: AuditEventRepository) -> None:
|
||||
"""Nhận kho sự kiện kiểm toán qua tham số — bản thật đọc đĩa, bản test nằm
|
||||
trong bộ nhớ.
|
||||
"""
|
||||
self._repository = repository
|
||||
|
||||
def query(self, kind: Optional[str] = None, ok: Optional[bool] = None,
|
||||
text: Optional[str] = None, sort_by: str = "ts",
|
||||
descending: bool = True, page: int = 1, page_size: int = 50) -> Page:
|
||||
"""Lọc theo loại/kết quả/từ khoá, sắp xếp rồi cắt thành một trang."""
|
||||
events = self._repository.load(kind=kind)
|
||||
|
||||
if ok is not None:
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
"""Cổng đọc dữ liệu của phân hệ Giám sát — hợp đồng, không phải cài đặt."""
|
||||
|
||||
@@ -13,7 +13,13 @@ from ..dto.audit_event_dto import AuditEventDTO
|
||||
|
||||
|
||||
class AuditEventRepository(Protocol):
|
||||
"""Cổng đọc nhật ký kiểm toán mà tầng application dùng.
|
||||
|
||||
Chỉ là hợp đồng: bản cài đặt thật đọc từ file cục bộ hoặc thư mục chia sẻ,
|
||||
còn test truyền vào bộ giả.
|
||||
"""
|
||||
def load(self, kind: Optional[str] = None) -> List[AuditEventDTO]:
|
||||
"""Đọc sự kiện kiểm toán, lọc theo loại nếu có."""
|
||||
...
|
||||
|
||||
|
||||
@@ -22,9 +28,11 @@ class CanonicalAuditEventRepository:
|
||||
— the only place this application service reaches into infrastructure."""
|
||||
|
||||
def __init__(self, audit_logger) -> None:
|
||||
"""Bọc bộ ghi nhật ký kiểm toán chuẩn để đọc sự kiện ra."""
|
||||
self._audit_logger = audit_logger
|
||||
|
||||
def load(self, kind: Optional[str] = None) -> List[AuditEventDTO]:
|
||||
"""Đọc sự kiện từ nhật ký và đổi sang DTO của tầng application."""
|
||||
events = self._audit_logger.load_events(kind=kind)
|
||||
return [AuditEventDTO.from_raw(e.to_dict()) for e in events]
|
||||
|
||||
@@ -33,9 +41,13 @@ class InMemoryAuditEventRepository:
|
||||
"""Test double — holds a fixed list of events, no file I/O."""
|
||||
|
||||
def __init__(self, events: List[AuditEventDTO]) -> None:
|
||||
"""Nhận sẵn danh sách sự kiện. Chép lại chứ không giữ tham chiếu: bên gọi sửa
|
||||
danh sách gốc thì kết quả test không được đổi theo.
|
||||
"""
|
||||
self._events = list(events)
|
||||
|
||||
def load(self, kind: Optional[str] = None) -> List[AuditEventDTO]:
|
||||
"""Trả về danh sách đã nạp sẵn, lọc theo loại nếu có."""
|
||||
if kind is None:
|
||||
return list(self._events)
|
||||
return [e for e in self._events if e.kind == kind]
|
||||
|
||||
Reference in New Issue
Block a user