feat(swarm): launch live swarm worker daemon under tmux supervisor

This commit is contained in:
operator
2026-10-05 17:29:16 +00:00
parent 7fc15eb127
commit 9e833d0c4b
7 changed files with 829 additions and 0 deletions
+117
View File
@@ -0,0 +1,117 @@
#!/usr/bin/env python3
"""Swarm worker daemon (bridge integrator).
Polls box for pending swarm slots, executes them, and posts results back.
Runs inside the `swarm-worker` tmux session via swarm-worker-supervise.sh.
Loop:
1. find_pending_slots() (poller.py)
2. execute_task(task_text) (executor.py)
3. post_result(swarm_id, slot, r) (reporter.py)
4. sleep POLL_INTERVAL, repeat
Safety:
- DRY_RUN=True means: never execute, never post. Log "would execute"
and skip. Flip to False only on explicit go-live authorization.
- Per-slot try/except: one bad slot never kills the loop.
- All activity logged to stdout (captured to the tmux log file).
"""
import logging
import os
import sys
import time
import traceback
# Sibling modules live in the same directory.
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from poller import find_pending_slots
from executor import execute_task
from reporter import post_result
POLL_INTERVAL = 60 # seconds between poll cycles
STALE_MINUTES = 5 # slots older than this with no attach are workable
# === SAFETY SWITCH ===
# True -> observe only: log what WOULD be done, execute/post nothing.
# False -> live mode. Flip only on explicit go-live authorization.
DRY_RUN = False
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [swarm-worker] %(levelname)s %(message)s",
datefmt="%Y-%m-%dT%H:%M:%S",
)
log = logging.getLogger("swarm-worker")
def process_slot(slot):
"""Execute one slot and report the result. Returns True on full success."""
swarm_id = slot.get("swarm_id")
slot_index = slot.get("slot_index")
task_text = slot.get("task_text") or ""
tag = "%s/%s" % (swarm_id, slot_index)
if DRY_RUN:
log.info("[dry-run] would execute slot %s (agent=%s, task %.80r)",
tag, slot.get("agent_label"), task_text)
return True
log.info("executing slot %s (agent=%s)", tag, slot.get("agent_label"))
try:
result = execute_task(task_text)
except Exception:
log.error("executor crashed on slot %s:\n%s", tag, traceback.format_exc())
result = {"success": False, "output": "",
"error": "executor crashed: see worker log"}
payload = {
"ok": bool(result.get("success")),
"output": result.get("output") or "",
"error": result.get("error"),
}
try:
posted = post_result(swarm_id, slot_index, payload)
except Exception:
log.error("reporter crashed on slot %s:\n%s", tag, traceback.format_exc())
posted = False
log.info("slot %s done: ok=%s posted=%s (%.1fs)",
tag, payload["ok"], posted,
float(result.get("duration_s") or 0.0))
return bool(payload["ok"]) and posted
def poll_cycle():
"""One poll cycle. Never raises."""
try:
slots = find_pending_slots(stale_minutes=STALE_MINUTES)
except Exception:
log.error("poller crashed:\n%s", traceback.format_exc())
return
if not slots:
log.info("poll: no pending slots")
return
log.info("poll: %d pending slot(s)", len(slots))
for slot in slots:
try:
process_slot(slot)
except Exception:
log.error("slot %s/%s unhandled error:\n%s",
slot.get("swarm_id"), slot.get("slot_index"),
traceback.format_exc())
def main():
log.info("starting (DRY_RUN=%s, interval=%ds)", DRY_RUN, POLL_INTERVAL)
while True:
try:
poll_cycle()
except Exception:
log.error("poll cycle crashed:\n%s", traceback.format_exc())
time.sleep(POLL_INTERVAL)
if __name__ == "__main__":
main()