Files
Elydora-Python-SDK/elydora/cli.py
T
ZacharyZhang-NYandClaude Opus 4.6 b42b670c2a Initial commit: Elydora Python SDK
Python SDK for Elydora agent operations, cryptographic signing, and audit trail integration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 18:03:09 -05:00

183 lines
5.9 KiB
Python

"""Elydora CLI — install, uninstall, and manage agent audit hooks.
Entry point: ``elydora`` console script (see pyproject.toml).
Uses only stdlib argparse — zero external dependencies for the CLI itself.
"""
from __future__ import annotations
import argparse
import os
import sys
from typing import Dict, Type
from .crypto import get_public_key_base64url
from .plugins.base import AgentPlugin, InstallConfig
from .plugins.registry import SUPPORTED_AGENTS, get_agent_names
from .plugins.hook_template import generate_guard_script
from .plugins.claudecode import ClaudeCodePlugin
from .plugins.cursor import CursorPlugin
from .plugins.gemini import GeminiPlugin
from .plugins.augment import AugmentPlugin
from .plugins.kiro import KiroPlugin
from .plugins.opencode import OpenCodePlugin
PLUGIN_MAP: Dict[str, Type[AgentPlugin]] = {
"claudecode": ClaudeCodePlugin,
"cursor": CursorPlugin,
"gemini": GeminiPlugin,
"augment": AugmentPlugin,
"kiro": KiroPlugin,
"opencode": OpenCodePlugin,
}
def _get_plugin(agent_name: str) -> AgentPlugin:
"""Instantiate the plugin for the given agent name."""
cls = PLUGIN_MAP.get(agent_name)
if cls is None:
print(f"Error: Unknown agent '{agent_name}'.", file=sys.stderr)
print(f"Supported agents: {', '.join(get_agent_names())}", file=sys.stderr)
sys.exit(1)
return cls()
def cmd_install(args: argparse.Namespace) -> None:
"""Handle the 'install' subcommand."""
agent_name: str = args.agent
# Derive public key to verify the private key is valid
try:
pub = get_public_key_base64url(args.private_key)
except Exception as exc:
print(f"Error: Invalid private key — {exc}", file=sys.stderr)
sys.exit(1)
print(f"Verified key pair (public key: {pub[:16]}...)")
# Generate and write the guard script
guard_dir = os.path.join(os.path.expanduser("~"), ".elydora", "hooks")
os.makedirs(guard_dir, exist_ok=True)
guard_script_path = os.path.join(guard_dir, f"{agent_name}-guard.py")
guard_script = generate_guard_script(agent_name)
with open(guard_script_path, "w", encoding="utf-8") as f:
f.write(guard_script)
try:
import stat
os.chmod(guard_script_path, os.stat(guard_script_path).st_mode | stat.S_IEXEC)
except Exception:
pass # chmod may fail on Windows
print(f" Guard script: {guard_script_path}")
config: InstallConfig = {
"org_id": args.org_id,
"agent_id": args.agent_id,
"private_key": args.private_key,
"kid": args.kid,
"base_url": args.base_url,
"guard_script_path": guard_script_path,
}
if args.token:
config["token"] = args.token
plugin = _get_plugin(agent_name)
plugin.install(config)
def cmd_uninstall(args: argparse.Namespace) -> None:
"""Handle the 'uninstall' subcommand."""
plugin = _get_plugin(args.agent)
plugin.uninstall()
# Remove the guard script
guard_script_path = os.path.join(
os.path.expanduser("~"), ".elydora", "hooks", f"{args.agent}-guard.py"
)
if os.path.exists(guard_script_path):
os.remove(guard_script_path)
print(f" Removed guard script: {guard_script_path}")
def cmd_status(args: argparse.Namespace) -> None:
"""Handle the 'status' subcommand."""
print("Elydora Agent Hook Status")
print("=" * 40)
for name in get_agent_names():
plugin = PLUGIN_MAP[name]()
st = plugin.status()
marker = "[installed]" if st["installed"] else "[not installed]"
display = SUPPORTED_AGENTS[name]["name"]
print(f" {display:20s} {marker:16s} {st['details']}")
def cmd_agents(args: argparse.Namespace) -> None:
"""Handle the 'agents' subcommand."""
print("Supported agents:")
for name in get_agent_names():
info = SUPPORTED_AGENTS[name]
print(f" {name:15s} {info['name']:20s} (hook: {info['hook_event']})")
def build_parser() -> argparse.ArgumentParser:
"""Build the argparse parser."""
parser = argparse.ArgumentParser(
prog="elydora",
description="Elydora — tamper-evident audit trail for AI agents",
)
parser.add_argument(
"--version", action="version", version="%(prog)s 1.0.0"
)
subparsers = parser.add_subparsers(dest="command", help="Available commands")
# install
install_parser = subparsers.add_parser("install", help="Install audit hook for an agent")
install_parser.add_argument("--agent", required=True, help="Agent name (e.g. claudecode, cursor)")
install_parser.add_argument("--org_id", required=True, help="Organization ID")
install_parser.add_argument("--agent_id", required=True, help="Agent ID")
install_parser.add_argument("--private_key", required=True, help="Base64url-encoded Ed25519 private key seed")
install_parser.add_argument("--kid", required=True, help="Key ID")
install_parser.add_argument("--token", default="", help="JWT bearer token (optional)")
install_parser.add_argument("--base_url", default="https://api.elydora.com", help="API base URL")
# uninstall
uninstall_parser = subparsers.add_parser("uninstall", help="Uninstall audit hook for an agent")
uninstall_parser.add_argument("--agent", required=True, help="Agent name")
# status
subparsers.add_parser("status", help="Show installation status of all agents")
# agents
subparsers.add_parser("agents", help="List supported agents")
return parser
def main() -> None:
"""CLI entry point."""
parser = build_parser()
args = parser.parse_args()
if args.command is None:
parser.print_help()
sys.exit(0)
handlers = {
"install": cmd_install,
"uninstall": cmd_uninstall,
"status": cmd_status,
"agents": cmd_agents,
}
handler = handlers.get(args.command)
if handler is None:
parser.print_help()
sys.exit(1)
handler(args)
if __name__ == "__main__":
main()