Compare commits
3 Commits
mimo/code/
...
fix/880
| Author | SHA1 | Date | |
|---|---|---|---|
| 07debcd612 | |||
| 3a1d5aa333 | |||
|
|
578bcd93ef |
184
scripts/lazarus.py
Executable file
184
scripts/lazarus.py
Executable file
@@ -0,0 +1,184 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Lazarus CLI — Mission invitation and cell spawning.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
lazarus invite [agent] --mission [id] --repo [url]
|
||||||
|
lazarus status --mission [id]
|
||||||
|
lazarus spawn --mission [id] --agent [name]
|
||||||
|
lazarus roster --mission [id]
|
||||||
|
|
||||||
|
Parent: #878, #880
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
MISSIONS_DIR = Path(os.path.expanduser("~/missions"))
|
||||||
|
ROSTER_FILE = "mission_roster.json"
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_missions_dir():
|
||||||
|
MISSIONS_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def mission_dir(mission_id: str) -> Path:
|
||||||
|
return MISSIONS_DIR / mission_id
|
||||||
|
|
||||||
|
|
||||||
|
def load_roster(mission_id: str) -> dict:
|
||||||
|
roster_path = mission_dir(mission_id) / ROSTER_FILE
|
||||||
|
if roster_path.exists():
|
||||||
|
return json.loads(roster_path.read_text())
|
||||||
|
return {"mission_id": mission_id, "agents": [], "created": datetime.now(timezone.utc).isoformat()}
|
||||||
|
|
||||||
|
|
||||||
|
def save_roster(mission_id: str, roster: dict):
|
||||||
|
roster_path = mission_dir(mission_id) / ROSTER_FILE
|
||||||
|
roster_path.write_text(json.dumps(roster, indent=2))
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_invite(args):
|
||||||
|
"""Invite an agent into a mission cell."""
|
||||||
|
ensure_missions_dir()
|
||||||
|
mid = args.mission
|
||||||
|
agent = args.agent
|
||||||
|
repo = args.repo
|
||||||
|
role = args.role or "write"
|
||||||
|
|
||||||
|
md = mission_dir(mid)
|
||||||
|
md.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Clone repo into mission cell
|
||||||
|
agent_dir = md / "agents" / agent
|
||||||
|
if not agent_dir.exists() and repo:
|
||||||
|
print(f"Cloning {repo} into {agent_dir}...")
|
||||||
|
agent_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
subprocess.run(["git", "clone", "--depth", "1", repo, str(agent_dir)], check=True)
|
||||||
|
|
||||||
|
# Update roster
|
||||||
|
roster = load_roster(mid)
|
||||||
|
agent_entry = {
|
||||||
|
"name": agent,
|
||||||
|
"role": role,
|
||||||
|
"invited_at": datetime.now(timezone.utc).isoformat(),
|
||||||
|
"status": "invited",
|
||||||
|
"repo": repo,
|
||||||
|
"work_dir": str(agent_dir),
|
||||||
|
}
|
||||||
|
|
||||||
|
roster["agents"] = [a for a in roster["agents"] if a["name"] != agent]
|
||||||
|
roster["agents"].append(agent_entry)
|
||||||
|
save_roster(mid, roster)
|
||||||
|
|
||||||
|
print(f"Invited {agent} to mission {mid} with role '{role}'")
|
||||||
|
print(f" Work dir: {agent_dir}")
|
||||||
|
print(f" Roster: {md / ROSTER_FILE}")
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_spawn(args):
|
||||||
|
"""Spawn an agent in a mission cell with Level 1 isolation."""
|
||||||
|
ensure_missions_dir()
|
||||||
|
mid = args.mission
|
||||||
|
agent = args.agent
|
||||||
|
|
||||||
|
md = mission_dir(mid)
|
||||||
|
agent_dir = md / "agents" / agent
|
||||||
|
|
||||||
|
if not agent_dir.exists():
|
||||||
|
print(f"ERROR: Agent {agent} not found in mission {mid}. Run 'lazarus invite' first.", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Update roster status
|
||||||
|
roster = load_roster(mid)
|
||||||
|
for a in roster["agents"]:
|
||||||
|
if a["name"] == agent:
|
||||||
|
a["status"] = "active"
|
||||||
|
a["spawned_at"] = datetime.now(timezone.utc).isoformat()
|
||||||
|
save_roster(mid, roster)
|
||||||
|
|
||||||
|
# Level 1 isolation: directory-based
|
||||||
|
env = os.environ.copy()
|
||||||
|
env["HERMES_HOME"] = str(agent_dir / ".hermes")
|
||||||
|
env["HERMES_MISSION_ID"] = mid
|
||||||
|
env["HERMES_MISSION_ROLE"] = next(
|
||||||
|
(a["role"] for a in roster["agents"] if a["name"] == agent), "write"
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"Spawning {agent} in mission {mid}")
|
||||||
|
print(f" HERMES_HOME: {env['HERMES_HOME']}")
|
||||||
|
print(f" Work dir: {agent_dir}")
|
||||||
|
print(f" Role: {env['HERMES_MISSION_ROLE']}")
|
||||||
|
|
||||||
|
cmd = ["hermes", "chat", "--mission-cell", str(agent_dir)]
|
||||||
|
print(f" Command: {' '.join(cmd)}")
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_status(args):
|
||||||
|
"""Show mission status."""
|
||||||
|
mid = args.mission
|
||||||
|
md = mission_dir(mid)
|
||||||
|
|
||||||
|
if not md.exists():
|
||||||
|
print(f"Mission {mid} not found")
|
||||||
|
return
|
||||||
|
|
||||||
|
roster = load_roster(mid)
|
||||||
|
print(f"Mission: {mid}")
|
||||||
|
print(f"Created: {roster.get('created', 'unknown')}")
|
||||||
|
print(f"Agents: {len(roster.get('agents', []))}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
for agent in roster.get("agents", []):
|
||||||
|
status = agent.get("status", "unknown")
|
||||||
|
role = agent.get("role", "unknown")
|
||||||
|
print(f" {agent['name']:<20} {role:<10} {status}")
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_roster(args):
|
||||||
|
"""Show mission roster."""
|
||||||
|
mid = args.mission
|
||||||
|
roster = load_roster(mid)
|
||||||
|
print(json.dumps(roster, indent=2))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Lazarus CLI -- Mission management")
|
||||||
|
sub = parser.add_subparsers(dest="command")
|
||||||
|
|
||||||
|
inv = sub.add_parser("invite", help="Invite agent to mission")
|
||||||
|
inv.add_argument("agent", help="Agent name")
|
||||||
|
inv.add_argument("--mission", required=True, help="Mission ID")
|
||||||
|
inv.add_argument("--repo", required=True, help="Git repo URL")
|
||||||
|
inv.add_argument("--role", choices=["lead", "write", "read", "audit"], default="write")
|
||||||
|
|
||||||
|
sp = sub.add_parser("spawn", help="Spawn agent in mission cell")
|
||||||
|
sp.add_argument("--mission", required=True, help="Mission ID")
|
||||||
|
sp.add_argument("--agent", required=True, help="Agent name")
|
||||||
|
|
||||||
|
st = sub.add_parser("status", help="Mission status")
|
||||||
|
st.add_argument("--mission", required=True, help="Mission ID")
|
||||||
|
|
||||||
|
ro = sub.add_parser("roster", help="Mission roster")
|
||||||
|
ro.add_argument("--mission", required=True, help="Mission ID")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.command == "invite":
|
||||||
|
cmd_invite(args)
|
||||||
|
elif args.command == "spawn":
|
||||||
|
cmd_spawn(args)
|
||||||
|
elif args.command == "status":
|
||||||
|
cmd_status(args)
|
||||||
|
elif args.command == "roster":
|
||||||
|
cmd_roster(args)
|
||||||
|
else:
|
||||||
|
parser.print_help()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user