feat(cli): add 'box swarm' domain (list, status, spawn, prune)

This commit is contained in:
operator
2026-10-05 16:04:56 +00:00
parent c74ebaa658
commit 983b5c668c
+125
View File
@@ -3109,6 +3109,101 @@ def cmd_ssh_show(args):
except Exception: except Exception:
sys.stdout.write(res.stdout) sys.stdout.write(res.stdout)
def cmd_swarm_list(args):
cmd = ["python3", str(BIN_DIR / "box-ctl.py"), "swarm-list"]
res = subprocess.run(cmd, capture_output=True, text=True)
if args.json:
sys.stdout.write(res.stdout)
return
try:
d = json.loads(res.stdout)
if d.get("ok"):
swarms = d.get("swarms", [])
print("\n" + c_bold(f"=== SWARMS ({len(swarms)}) ===") + "\n")
headers = ["ID", "STATUS", "LABEL", "TOTAL", "DONE", "RUNNING", "FAILED", "CREATED"]
rows = []
for sw in swarms:
st = sw.get("status")
st_color = c_green(st) if st == "completed" else (c_yellow(st) if st in ("running", "partial") else c_dim(st))
rows.append([
c_cyan(sw.get("swarm_id", "")[:18]),
st_color,
(sw.get("label") or "-")[:20],
str(sw.get("count", 0)),
str(sw.get("done", 0)),
str(sw.get("running", 0)),
str(sw.get("failed", 0)),
(sw.get("created_ts") or "")[:16]
])
print_table(headers, rows)
print("\n" + c_dim(" Spawn swarm: box swarm spawn <count> <task> [--label L]") + "\n")
else:
print(c_red(f"✗ Failed: {d.get('error')}"))
except Exception:
sys.stdout.write(res.stdout)
def cmd_swarm_status(args):
cmd = ["python3", str(BIN_DIR / "box-ctl.py"), "swarm-status", args.swarm_id]
res = subprocess.run(cmd, capture_output=True, text=True)
if args.json:
sys.stdout.write(res.stdout)
return
try:
d = json.loads(res.stdout)
if d.get("ok"):
print(c_bold(f"\n=== SWARM STATUS: {args.swarm_id} ==="))
print(f" Status: {d.get('status')}")
print(f" Counts: {d.get('counts')}")
slots = d.get("slots", [])
print(f"\n Slots ({len(slots)}):")
for s in slots:
res_txt = s.get("result")
res_prev = (str(res_txt)[:50] + "...") if res_txt and len(str(res_txt)) > 50 else str(res_txt)
print(f" Slot {s.get('slot')}: [{s.get('status')}] agent={s.get('agent_id')} subagent={s.get('subagent_session_id')} res={res_prev}")
print()
else:
print(c_red(f"✗ Failed: {d.get('error')}"))
except Exception:
sys.stdout.write(res.stdout)
def cmd_swarm_spawn(args):
cmd = ["python3", str(BIN_DIR / "box-ctl.py"), "swarm-spawn", str(args.count), args.task]
if args.label:
cmd.extend(["--label", args.label])
res = subprocess.run(cmd, capture_output=True, text=True)
if args.json:
sys.stdout.write(res.stdout)
return
try:
d = json.loads(res.stdout)
if d.get("ok"):
print(c_green(f"✓ Spawned swarm: {d.get('swarm_id')} ({d.get('count')} slots)"))
else:
print(c_red(f"✗ Failed: {d.get('error')} ({d.get('code')})"))
except Exception:
sys.stdout.write(res.stdout)
def cmd_swarm_prune(args):
cmd = ["python3", str(BIN_DIR / "box-ctl.py"), "swarm-prune", "--stale-hours", str(args.stale_hours)]
if args.confirm:
cmd.append("--confirm")
res = subprocess.run(cmd, capture_output=True, text=True)
if args.json:
sys.stdout.write(res.stdout)
return
try:
d = json.loads(res.stdout)
if d.get("ok"):
print(c_green(f"✓ Pruned {d.get('archived_count')} swarms older than {d.get('stale_hours')}h to 'archived'"))
else:
print(c_yellow(f"! {d.get('error')} (use --confirm)"))
except Exception:
sys.stdout.write(res.stdout)
def build_parser(): def build_parser():
common = argparse.ArgumentParser(add_help=False) common = argparse.ArgumentParser(add_help=False)
common.add_argument("--json", action="store_true", help="Output machine-readable JSON") common.add_argument("--json", action="store_true", help="Output machine-readable JSON")
@@ -3499,6 +3594,24 @@ def build_parser():
p_dep_sub.add_argument("prompt", help="Task prompt for the subagent") p_dep_sub.add_argument("prompt", help="Task prompt for the subagent")
p_dep_sub.add_argument("--wait", type=int, default=30, help="Seconds to wait for subagent response") p_dep_sub.add_argument("--wait", type=int, default=30, help="Seconds to wait for subagent response")
# Domain: SWARM
p_swarm = subparsers.add_parser("swarm", parents=[common], help="Manage multi-agent swarm tasks and worker allocations")
swarm_sub = p_swarm.add_subparsers(dest="action")
p_sw_list = swarm_sub.add_parser("list", parents=[common], help="List all swarms and slot rollups")
p_sw_status = swarm_sub.add_parser("status", parents=[common], help="Show status and slot details for a swarm")
p_sw_status.add_argument("swarm_id", help="Swarm identifier (sw-...)")
p_sw_spawn = swarm_sub.add_parser("spawn", parents=[common], help="Spawn a new multi-slot worker swarm")
p_sw_spawn.add_argument("count", type=int, help="Number of worker slots (1-50)")
p_sw_spawn.add_argument("task", help="Task prompt for the swarm workers")
p_sw_spawn.add_argument("--label", default=None, help="Optional tracking label")
p_sw_prune = swarm_sub.add_parser("prune", parents=[common], help="Archive stale partial/terminal swarms")
p_sw_prune.add_argument("--stale-hours", type=float, default=6.0, help="Hours of inactivity before pruning (default: 6)")
p_sw_prune.add_argument("--confirm", action="store_true", help="Confirm execution of pruning")
# Domain: subagent (Direct alias for spawning & managing subagents) # Domain: subagent (Direct alias for spawning & managing subagents)
p_subagent = subparsers.add_parser("subagent", parents=[common], help="Spawn sub-agent session and dispatch task") p_subagent = subparsers.add_parser("subagent", parents=[common], help="Spawn sub-agent session and dispatch task")
subagent_sub = p_subagent.add_subparsers(dest="action") subagent_sub = p_subagent.add_subparsers(dest="action")
@@ -3691,6 +3804,18 @@ def main():
cmd_loop_strat(args) cmd_loop_strat(args)
elif args.domain == "vars": elif args.domain == "vars":
cmd_loop_vars(args) cmd_loop_vars(args)
elif args.domain == "swarm":
act = getattr(args, "action", None)
if not act or act == "list":
cmd_swarm_list(args)
elif act == "status":
cmd_swarm_status(args)
elif act == "spawn":
cmd_swarm_spawn(args)
elif act == "prune":
cmd_swarm_prune(args)
else:
p_swarm.print_help()
elif args.domain in ("deploy", "subagent"): elif args.domain in ("deploy", "subagent"):
if args.domain == "subagent": if args.domain == "subagent":
args.action = "subagent" args.action = "subagent"