Files
workinf_Blender_Wasm/my-tools/run_blender_web_scheduler.py
2026-09-02 18:35:58 +08:00

97 lines
3.5 KiB
Python
Executable File

#!/usr/bin/env python3
"""Run the repository's Blender/WBV2 unattended scheduler.
It is the project-local wrapper for copied launcher scripts. It does not start
an interactive agent, use a second ledger, or emulate
Blender behavior. The authoritative Node scheduler owns queue validation,
guardian/supervisor execution, evidence handoff, commit and push rules.
"""
from __future__ import annotations
import argparse
import json
import os
import signal
import subprocess
from datetime import datetime, timezone
from wbv2_runtime import ROOT, scheduler_args
STATUS_PATH = ROOT / "tests/golden/WBV2/my-tools-scheduler.json"
PID_PATH = ROOT / "tests/golden/WBV2/my-tools-scheduler.pid"
def write_status(**values: object) -> None:
STATUS_PATH.parent.mkdir(parents=True, exist_ok=True)
payload = {"schemaVersion": 1, "updatedAt": datetime.now(timezone.utc).isoformat(), **values}
temporary = STATUS_PATH.with_suffix(f".tmp-{os.getpid()}")
temporary.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
temporary.replace(STATUS_PATH)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--max-iterations", "--max-cycles", type=int, default=0)
parser.add_argument("--poll-ms", type=int, default=1000)
parser.add_argument("--push-retries", type=int, default=6)
parser.add_argument(
"--commit",
action="store_true",
default=os.getenv("WBV2_AUTO_COMMIT", "1") != "0",
help="commit the exact handoff files after a successful task (default: on)",
)
parser.add_argument(
"--push",
action="store_true",
default=os.getenv("WBV2_AUTO_PUSH", "1") != "0",
help="push and verify origin/main after committing (default: on)",
)
parser.add_argument("--preflight-only", action="store_true")
args = parser.parse_args()
if args.max_iterations < 0 or args.poll_ms < 0 or args.push_retries < 0:
parser.error("iteration, poll and retry values must be non-negative")
if args.push and not args.commit:
parser.error("--push requires --commit")
command = scheduler_args(
commit=args.commit,
push=args.push,
max_iterations=args.max_iterations,
preflight_only=args.preflight_only,
)
command.extend(["--poll-ms", str(args.poll_ms), "--push-retries", str(args.push_retries)])
print("WBV2 scheduler:", " ".join(command), flush=True)
PID_PATH.parent.mkdir(parents=True, exist_ok=True)
if PID_PATH.exists():
try:
existing = int(PID_PATH.read_text(encoding="ascii").strip())
os.kill(existing, 0)
except (OSError, ValueError):
PID_PATH.unlink(missing_ok=True)
else:
parser.error(f"scheduler wrapper already running: pid={existing}")
PID_PATH.write_text(f"{os.getpid()}\n", encoding="ascii")
write_status(status="starting", pid=os.getpid(), command=command)
process = subprocess.Popen(command, cwd=ROOT, start_new_session=True)
def forward(signum: int, _frame: object) -> None:
try:
os.killpg(process.pid, signum)
except ProcessLookupError:
pass
signal.signal(signal.SIGINT, forward)
signal.signal(signal.SIGTERM, forward)
try:
code = process.wait()
write_status(status="complete" if code == 0 else "blocked", pid=os.getpid(), childPid=process.pid, exitCode=code)
return code
finally:
PID_PATH.unlink(missing_ok=True)
if __name__ == "__main__":
raise SystemExit(main())