Automated salvage commit — agent session ended (exit 124). Work in progress, may need continuation.
163 lines
5.4 KiB
Python
163 lines
5.4 KiB
Python
"""Lazarus CLI — invite agents, manage mission cells."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import platform
|
|
import sys
|
|
import uuid
|
|
from pathlib import Path
|
|
|
|
from lazarus.cell import MissionCell, create_cell, load_cell
|
|
from lazarus.isolation import setup_level1_isolation
|
|
from lazarus.roster import DEFAULT_REGISTRY_PATH, MissionRoster
|
|
|
|
|
|
def _default_base_path() -> Path:
|
|
if platform.system() == "Linux":
|
|
return Path("/var/missions")
|
|
return Path.home() / ".nexus" / "missions"
|
|
|
|
|
|
# ─── command handlers ─────────────────────────────────────────────────────────
|
|
|
|
|
|
def cmd_invite(args: argparse.Namespace) -> int:
|
|
"""Create or load a mission cell and invite an agent."""
|
|
base_path = Path(args.base_path) if args.base_path else _default_base_path()
|
|
mission_id: str = args.mission or str(uuid.uuid4())
|
|
roster = MissionRoster(Path(args.registry) if args.registry else DEFAULT_REGISTRY_PATH)
|
|
|
|
# Create or load cell
|
|
cell_dir = base_path / mission_id
|
|
if (cell_dir / "cell.json").exists():
|
|
cell = load_cell(cell_dir)
|
|
else:
|
|
cell = create_cell(
|
|
mission_id=mission_id,
|
|
name=args.name or mission_id,
|
|
repo_url=args.repo,
|
|
base_path=base_path,
|
|
)
|
|
|
|
# Level 1 isolation
|
|
setup_level1_isolation(cell)
|
|
|
|
# Register mission and invite agent
|
|
roster.add_mission(cell)
|
|
entry = roster.invite_agent(mission_id, args.agent, role=args.role)
|
|
roster.save()
|
|
|
|
summary = {
|
|
"mission_id": mission_id,
|
|
"agent": args.agent,
|
|
"role": entry.role,
|
|
"status": entry.status,
|
|
"cell_path": cell.cell_path,
|
|
"repo_url": cell.repo_url,
|
|
"invited_at": entry.invited_at,
|
|
}
|
|
print(json.dumps(summary, indent=2))
|
|
return 0
|
|
|
|
|
|
def cmd_list(args: argparse.Namespace) -> int:
|
|
"""List missions, optionally filtered by state."""
|
|
roster = MissionRoster(Path(args.registry) if args.registry else DEFAULT_REGISTRY_PATH)
|
|
missions = roster.list_missions(state=args.state)
|
|
print(json.dumps(missions, indent=2))
|
|
return 0
|
|
|
|
|
|
def cmd_status(args: argparse.Namespace) -> int:
|
|
"""Show details of a specific mission."""
|
|
roster = MissionRoster(Path(args.registry) if args.registry else DEFAULT_REGISTRY_PATH)
|
|
mission = roster.get_mission(args.mission_id)
|
|
if mission is None:
|
|
print(f"Mission {args.mission_id!r} not found.", file=sys.stderr)
|
|
return 1
|
|
agents = roster.list_agents(args.mission_id)
|
|
output = dict(mission)
|
|
output["agent_count"] = len(agents)
|
|
output["agents"] = [a.to_dict() for a in agents]
|
|
print(json.dumps(output, indent=2))
|
|
return 0
|
|
|
|
|
|
def cmd_depart(args: argparse.Namespace) -> int:
|
|
"""Mark an agent as departed from a mission."""
|
|
roster = MissionRoster(Path(args.registry) if args.registry else DEFAULT_REGISTRY_PATH)
|
|
try:
|
|
entry = roster.depart_agent(args.mission, args.agent)
|
|
except KeyError as exc:
|
|
print(f"Error: {exc}", file=sys.stderr)
|
|
return 1
|
|
roster.save()
|
|
print(json.dumps(entry.to_dict(), indent=2))
|
|
return 0
|
|
|
|
|
|
# ─── argument parsing ─────────────────────────────────────────────────────────
|
|
|
|
|
|
def _build_parser() -> argparse.ArgumentParser:
|
|
parser = argparse.ArgumentParser(
|
|
prog="lazarus",
|
|
description="Lazarus Pit — agent invitation and mission cell management",
|
|
)
|
|
parser.add_argument(
|
|
"--registry",
|
|
default=None,
|
|
help="Path to mission roster JSON (default: ~/.nexus/mission-roster.json)",
|
|
)
|
|
|
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
|
|
# invite
|
|
p_invite = sub.add_parser("invite", help="Invite an agent to a mission")
|
|
p_invite.add_argument("agent", help="Agent name to invite")
|
|
p_invite.add_argument("--mission", default=None, help="Mission ID (UUID generated if omitted)")
|
|
p_invite.add_argument("--repo", required=True, help="Repository URL for the mission")
|
|
p_invite.add_argument("--base-path", default=None, help="Base directory for mission cells")
|
|
p_invite.add_argument("--role", default="contributor", help="Agent role (default: contributor)")
|
|
p_invite.add_argument("--name", default=None, help="Human-readable mission name")
|
|
|
|
# list
|
|
p_list = sub.add_parser("list", help="List missions")
|
|
p_list.add_argument("--state", default=None, help="Filter by state (pending/active/archived/destroyed)")
|
|
|
|
# status
|
|
p_status = sub.add_parser("status", help="Show mission status")
|
|
p_status.add_argument("mission_id", help="Mission ID")
|
|
|
|
# depart
|
|
p_depart = sub.add_parser("depart", help="Mark agent as departed")
|
|
p_depart.add_argument("agent", help="Agent name")
|
|
p_depart.add_argument("--mission", required=True, help="Mission ID")
|
|
|
|
return parser
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
parser = _build_parser()
|
|
args = parser.parse_args(argv)
|
|
|
|
dispatch = {
|
|
"invite": cmd_invite,
|
|
"list": cmd_list,
|
|
"status": cmd_status,
|
|
"depart": cmd_depart,
|
|
}
|
|
|
|
handler = dispatch.get(args.command)
|
|
if handler is None:
|
|
parser.print_help()
|
|
return 1
|
|
|
|
return handler(args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|