fix(web): expose unattended planner heartbeat
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

This commit is contained in:
mes123456
2026-09-05 18:38:27 +08:00
parent 9ec6d4d28f
commit f5f77492f5
2 changed files with 25 additions and 11 deletions

View File

@@ -39,6 +39,14 @@ def main() -> int:
"watchdogStatus": read_json(CONTROL_ROOT / "my-tools-watchdog.json"),
"schedulerWrapperStatus": read_json(CONTROL_ROOT / "my-tools-scheduler.json"),
}
watchdog = status["myTools"]["watchdogStatus"]
watchdog_pid = watchdog.get("pid") if isinstance(watchdog, dict) else None
watchdog_alive = isinstance(watchdog_pid, int) and (Path("/proc") / str(watchdog_pid)).exists()
status["myTools"]["watchdogAlive"] = watchdog_alive
status["unattendedState"] = (
"PLANNING" if status.get("state") == "IDLE" and watchdog_alive and watchdog.get("status") == "waiting_for_source_batch"
else status.get("state", "UNKNOWN")
)
status["progress"] = project_progress()
status["fastPath"] = fast_path_status(status)
if args.json:
@@ -53,7 +61,7 @@ def main() -> int:
if args.watch:
print("\x1b[2J\x1b[H", end="")
print("Blender Web/WBV2 scheduler")
print(f" state: {status.get('state', 'UNKNOWN')}")
print(f" state: {status.get('unattendedState', status.get('state', 'UNKNOWN'))}")
print(f" phase: {status.get('phase', 'unknown')}")
queue = status.get("queue", {})
print(f" current task: {queue.get('task') or 'none'}")
@@ -65,12 +73,15 @@ def main() -> int:
print(f" guardian: pid={guardian.get('pid') or 'none'} alive={guardian.get('alive', False)} state={guardian.get('state') or 'unknown'}")
print(f" supervisor: pid={supervisor.get('pid') or 'none'} alive={supervisor.get('alive', False)} state={supervisor.get('state') or 'unknown'}")
watchdog = status.get("myTools", {}).get("watchdogStatus", {})
print(f" watchdog: pid={watchdog.get('pid') or 'external'} status={watchdog.get('status') or 'unknown'} reason={watchdog.get('reason') or 'none'} task={watchdog.get('task') or 'none'}")
print(f" watchdog: pid={watchdog.get('pid') or 'none'} alive={status.get('myTools', {}).get('watchdogAlive', False)} status={watchdog.get('status') or 'unknown'} reason={watchdog.get('reason') or 'none'} task={watchdog.get('task') or 'none'}")
print(f" latest handoff: {status.get('latestHandoff') or 'none'}")
print(f" local HEAD: {status.get('localHead') or 'unknown'}")
for line in format_progress(status["progress"], status["fastPath"]):
print(line)
print(f" message: {status.get('message', '')}")
if status.get("unattendedState") == "PLANNING":
print(" message: PLANNING: watchdog heartbeat is active; exact source gaps are queued without false coverage.")
else:
print(f" message: {status.get('message', '')}")
if not args.watch:
return 0
try:

View File

@@ -32,14 +32,17 @@ def now() -> str:
def write_json(path: Path, value: dict) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
try:
current = json.loads(path.read_text(encoding="utf-8"))
stable_current = {key: item for key, item in current.items() if key != "updatedAt"}
stable_next = {key: item for key, item in value.items() if key != "updatedAt"}
if stable_current == stable_next:
return
except (OSError, json.JSONDecodeError):
pass
if path == WATCHDOG_STATUS:
value = {**value, "pid": os.getpid()}
else:
try:
current = json.loads(path.read_text(encoding="utf-8"))
stable_current = {key: item for key, item in current.items() if key != "updatedAt"}
stable_next = {key: item for key, item in value.items() if key != "updatedAt"}
if stable_current == stable_next:
return
except (OSError, json.JSONDecodeError):
pass
temporary = path.with_suffix(path.suffix + f".tmp-{os.getpid()}")
temporary.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
temporary.replace(path)