#!/usr/bin/env python3 """ Generate systemd service file for hermes-service. Usage: python3 install-service.py [--user USER] [--group GROUP] [--install] """ import os import sys import argparse import shutil # Resolve paths dynamically SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) SERVICE_MODULE = SCRIPT_DIR MAIN_PY = os.path.join(SERVICE_MODULE, "main.py") # Auto-detect Python venv if it exists VENV_PYTHON = os.path.join(SERVICE_MODULE, ".venv", "bin", "python3") SYSTEM_PYTHON = shutil.which("python3") if os.path.exists(VENV_PYTHON): PYTHON_EXEC = VENV_PYTHON else: PYTHON_EXEC = SYSTEM_PYTHON or "/usr/bin/python3" PORT = 9121 USER = "hermesai" GROUP = "hermesai" SERVICE_CONTENT_TEMPLATE = """[Unit] Description=Hermes Service - AI Agent HTTP API After=network.target [Service] Type=simple User={user} Group={group} WorkingDirectory={workdir} ExecStart={python} {main_py} Restart=always RestartSec=5 StandardOutput=journal StandardError=journal # Environment Environment="HOME={home}" Environment="PYTHONUNBUFFERED=1" # Security hardening NoNewPrivileges=true ProtectSystem=strict ProtectHome=read-only ReadWritePaths={users_path} {data_path} [Install] WantedBy=multi-user.target """ def generate(args): parser = argparse.ArgumentParser(description="Generate hermes-service systemd service") parser.add_argument("--user", default=USER, help="Run as this user (default: hermesai)") parser.add_argument("--group", default=GROUP, help="Run as this group (default: hermesai)") parser.add_argument("--port", type=int, default=PORT, help="Service port (default: 9121)") parser.add_argument("--install", action="store_true", help="Copy to /etc/systemd/system and enable") parser.add_argument("--output", default=None, help="Output file path (default: ./hermes-service.service)") args = parser.parse_args() return args def main(): args = generate(sys.argv[1:]) user = args.user group = args.group port = args.port install = args.install service_name = "hermes-service" output = args.output or os.path.join(SERVICE_MODULE, f"{service_name}.service") # Validate if not os.path.exists(MAIN_PY): print(f"ERROR: main.py not found at {MAIN_PY}") sys.exit(1) # Generate service content content = SERVICE_CONTENT_TEMPLATE.format( user=user, group=group, workdir=SERVICE_MODULE, python=PYTHON_EXEC, main_py=MAIN_PY, home=os.environ.get('HOME', '/home/hermesai'), users_path=os.path.join(os.environ.get('HOME', '/home/hermesai'), 'users'), data_path=os.path.join(SERVICE_MODULE, 'data'), ) # Write service file with open(output, "w") as f: f.write(content) print(f"Service file written to: {output}") print(f" Python: {PYTHON_EXEC}") print(f" WorkingDir: {SERVICE_MODULE}") print(f" User/Group: {user}/{group}") print(f" Port: {port}") print(f" Main: {MAIN_PY}") print() if install: dest = f"/etc/systemd/system/{service_name}.service" if os.geteuid() != 0: print(f"ERROR: --install requires root privileges. Run with sudo.") sys.exit(1) shutil.copy2(output, dest) os.chmod(dest, 0o644) print(f"Installed to: {dest}") print() print("Next steps:") print(f" sudo systemctl daemon-reload") print(f" sudo systemctl enable {service_name}") print(f" sudo systemctl start {service_name}") print(f" sudo systemctl status {service_name}") else: print("To install and enable:") print(f" sudo python3 {os.path.basename(__file__)} --install") if __name__ == "__main__": main()