feat(swarm): launch live swarm worker daemon under tmux supervisor
This commit is contained in:
Executable
+87
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Swarm worker -> main-loop visibility bridge (Bridge Builder 5/5).
|
||||
|
||||
After the worker records a slot result via `box swarm-report` (see
|
||||
reporter.py), this posts a short human-readable completion note to the
|
||||
slot's own sidechat. The note deliberately carries NO `[JOB ` tag, so
|
||||
self_main_loop.py's check_sidechats() does not filter it: the next
|
||||
main-loop tick picks it up, includes it in the agent's digest, and the
|
||||
completion becomes visible in the same surface as everything else.
|
||||
That is the "answering via the main loop" path.
|
||||
|
||||
Design doc: bin/swarm_worker/mainloop-bridge.md
|
||||
|
||||
Runs on bl. Never auto-wired into any loop; call explicitly from the
|
||||
worker daemon after post_result() succeeds. Dry-run mode posts nothing.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
BIN = "/home/super/Projects/NetVM/bin"
|
||||
DM_PY = BIN + "/dm.py"
|
||||
NOTE_CHARS = 200
|
||||
|
||||
|
||||
def notify_via_mainloop(swarm_id, slot_index, message, worker_id="swarm-worker",
|
||||
dry_run=False):
|
||||
"""Post a swarm-slot completion note visible to the main loop.
|
||||
|
||||
Args:
|
||||
swarm_id: e.g. "sw-20261005-151307-a222"
|
||||
slot_index: int slot number
|
||||
message: human-readable summary (first NOTE_CHARS chars used)
|
||||
worker_id: sender identity for dm.py --agent
|
||||
dry_run: if True, print what WOULD be sent without sending.
|
||||
|
||||
Returns:
|
||||
True on success (or dry-run), False on failure (logged, not raised).
|
||||
"""
|
||||
target_name = "sw-%s-s%s" % (swarm_id, slot_index)
|
||||
summary = (message or "").strip().replace("\n", " ")[:NOTE_CHARS]
|
||||
note = "[SWARM-DONE %s/%s] %s" % (swarm_id, slot_index, summary)
|
||||
|
||||
try:
|
||||
sys.path.insert(0, BIN)
|
||||
import dm
|
||||
uuid = dm.resolve_sidechat_target(target_name)
|
||||
except Exception as e:
|
||||
print("notify_via_mainloop: target resolve failed for %s: %s"
|
||||
% (target_name, e), file=sys.stderr)
|
||||
return False
|
||||
if not uuid:
|
||||
print("notify_via_mainloop: no UUID for %s" % target_name,
|
||||
file=sys.stderr)
|
||||
return False
|
||||
|
||||
cmd = [sys.executable, DM_PY, "send",
|
||||
"--agent", worker_id,
|
||||
"--to", worker_id,
|
||||
"--target", uuid,
|
||||
note]
|
||||
if dry_run:
|
||||
print("DRY-RUN would run: %s" % " ".join(cmd))
|
||||
return True
|
||||
try:
|
||||
r = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
|
||||
except Exception as e:
|
||||
print("notify_via_mainloop: dm.py exec failed: %s" % e,
|
||||
file=sys.stderr)
|
||||
return False
|
||||
if r.returncode != 0:
|
||||
tail = ((r.stderr or "") + (r.stdout or "")).strip()[-300:]
|
||||
print("notify_via_mainloop: dm.py rc=%d: %s" % (r.returncode, tail),
|
||||
file=sys.stderr)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
live = "--live" in sys.argv
|
||||
args = [a for a in sys.argv[1:] if a != "--live"]
|
||||
if len(args) < 3:
|
||||
print("usage: mainloop_notify.py <swarm-id> <slot> \"<message>\" [--live]")
|
||||
print("default is DRY-RUN (posts nothing).")
|
||||
sys.exit(2)
|
||||
ok = notify_via_mainloop(args[0], args[1], args[2], dry_run=not live)
|
||||
print("notified" if ok else "FAILED")
|
||||
Reference in New Issue
Block a user