Ship War Room UX, CEO inbox, mobile chat, and Agents NAS workspace.
Make A2A togglable, keep reply context while reading, route agent data to //10.4.7.11/share/Agents, and polish themes plus export-intel sync cancel.
This commit is contained in:
@@ -11,6 +11,39 @@ STATUS_PATH = Path("/tmp/export-intel-sync-status.json")
|
||||
LOG_PATH = Path("/tmp/export-intel-sync-progress.jsonl")
|
||||
LOCK_PATH = Path("/tmp/export-intel-sync.lock")
|
||||
|
||||
CANCEL_PATH = Path("/tmp/export-intel-sync-cancel.flag")
|
||||
|
||||
|
||||
class SyncCancelled(Exception):
|
||||
"""Raised when a running sync job is cancelled by the user."""
|
||||
|
||||
|
||||
def request_cancel(reason: str = "user") -> dict[str, Any]:
|
||||
CANCEL_PATH.write_text(json.dumps({"ts": _now(), "reason": reason}))
|
||||
log_event("cancel_requested", reason=reason)
|
||||
# Mark running jobs as cancelling
|
||||
status = _load_status()
|
||||
for job in status.get("jobs", {}).values():
|
||||
if job.get("status") == "running":
|
||||
job["status"] = "cancelling"
|
||||
job["message"] = "Annuleren…"
|
||||
job["updated_at"] = _now()
|
||||
_save_status(status)
|
||||
return {"ok": True, "cancel_requested": True}
|
||||
|
||||
|
||||
def clear_cancel() -> None:
|
||||
CANCEL_PATH.unlink(missing_ok=True)
|
||||
|
||||
|
||||
def is_cancel_requested() -> bool:
|
||||
return CANCEL_PATH.exists()
|
||||
|
||||
|
||||
def raise_if_cancelled() -> None:
|
||||
if is_cancel_requested():
|
||||
raise SyncCancelled("Sync geannuleerd door gebruiker")
|
||||
|
||||
_active_job_id: Optional[str] = None
|
||||
|
||||
|
||||
@@ -68,6 +101,7 @@ def _clear_lock() -> None:
|
||||
|
||||
|
||||
def start_job(job_type: str, label: str, total: int = 0, meta: Optional[dict[str, Any]] = None) -> str:
|
||||
clear_cancel()
|
||||
job_id = f"{job_type}-{_now():.0f}"
|
||||
status = _load_status()
|
||||
job = {
|
||||
@@ -138,7 +172,10 @@ def finish_job(job_id: str, result: Optional[dict[str, Any]] = None, error: Opti
|
||||
job = status["jobs"].get(job_id)
|
||||
if not job:
|
||||
return
|
||||
job["status"] = "error" if error else "completed"
|
||||
if error and "geannuleerd" in str(error).lower():
|
||||
job["status"] = "cancelled"
|
||||
else:
|
||||
job["status"] = "error" if error else "completed"
|
||||
job["updated_at"] = _now()
|
||||
job["finished_at"] = _now()
|
||||
job["percent"] = 100 if not error else job.get("percent", 0)
|
||||
@@ -151,9 +188,10 @@ def finish_job(job_id: str, result: Optional[dict[str, Any]] = None, error: Opti
|
||||
log_event("job_finish", job_id=job_id, status=job["status"], error=error)
|
||||
if get_active_job() == job_id:
|
||||
set_active_job(None)
|
||||
running = [j for j in status["jobs"].values() if j.get("status") == "running"]
|
||||
running = [j for j in status["jobs"].values() if j.get("status") in ("running", "cancelling")]
|
||||
if not running:
|
||||
_clear_lock()
|
||||
clear_cancel()
|
||||
|
||||
|
||||
def tail_log(limit: int = 40) -> list[dict[str, Any]]:
|
||||
@@ -172,7 +210,7 @@ def tail_log(limit: int = 40) -> list[dict[str, Any]]:
|
||||
def get_monitor_snapshot(db_counts: Optional[dict[str, Any]] = None) -> dict[str, Any]:
|
||||
status = _load_status()
|
||||
jobs = status.get("jobs", {})
|
||||
active = [j for j in jobs.values() if j.get("status") == "running"]
|
||||
active = [j for j in jobs.values() if j.get("status") in ("running", "cancelling")]
|
||||
recent = [jobs[jid] for jid in status.get("history", [])[:8] if jid in jobs and jobs[jid].get("status") != "running"]
|
||||
lock = _read_json(LOCK_PATH, None) if LOCK_PATH.exists() else None
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user