From 6e9afe3e54ab6fb0162393150b7892655b4561d4 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Thu, 24 Sep 2026 18:19:13 +0200 Subject: refactor(node): move the CLI out of daemon.py into cli/ Each `if args.command == ...` branch of main() becomes a function in cli/ (one module per family of verbs); the parser, the process setup and a VERBS table go to cli/parser.py and cli/dispatch.py. daemon.main runs the verb or starts the daemon. Tests patch the CLI through conftest.patch_cli; the CLI golden is unchanged. Co-Authored-By: Claude Opus 5.5 --- .../meshbay-node/src/meshbay_node/cli/lifecycle.py | 135 +++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 packages/meshbay-node/src/meshbay_node/cli/lifecycle.py (limited to 'packages/meshbay-node/src/meshbay_node/cli/lifecycle.py') diff --git a/packages/meshbay-node/src/meshbay_node/cli/lifecycle.py b/packages/meshbay-node/src/meshbay_node/cli/lifecycle.py new file mode 100644 index 0000000..6341561 --- /dev/null +++ b/packages/meshbay-node/src/meshbay_node/cli/lifecycle.py @@ -0,0 +1,135 @@ +"""Starting, stopping and reloading the node: systemd here, the Startup +launcher or the service task on Windows.""" + +import sys + +from meshbay_node.cli.api import _daemon_api, _systemctl_user +from meshbay_node.config import DEFAULT_CONFIG_PATH, load_config + + +def reload(args) -> None: + if sys.platform == "win32": + # No systemd, no SIGHUP: the daemon exposes a hot reload on its + # own loopback API (the same one ops.reload_config drives). + cfg = load_config(args.config or DEFAULT_CONFIG_PATH) + _daemon_api(cfg, "/api/reload", method="POST") + print("sent reload to the running node") + return + _systemctl_user( + "reload", "meshbay-node", + not_running_hint="Node is not running as a systemd unit — start it " + "with: systemctl --user start meshbay-node", + success="sent reload to meshbay-node", + watch="watch the result: journalctl --user -u meshbay-node -f") + return + + +def restart_daemon(args) -> None: + if sys.platform == "win32": + from meshbay_node.platform import ( + autostart_end, + autostart_run, + service_end, + service_run, + service_status, + ) + if service_status()["installed"]: + service_end() + service_run() + print("restarted the node (service task)") + return + autostart_end() # kill whatever is running now + try: + autostart_run() + except RuntimeError as e: + print(f"Could not restart: {e}. Stop the daemon (Ctrl+C) and " + "relaunch it from where meshbay-node is on PATH.") + sys.exit(1) + print("restarted the node") + return + _systemctl_user( + "restart", "meshbay-node", + not_running_hint="meshbay-node is not installed as a systemd unit — " + "see packaging/systemd/", + success="meshbay-node restarted via systemd", + watch="check status: systemctl --user status meshbay-node\n" + "watch logs: journalctl --user -u meshbay-node -f") + return + + +def autostart(args) -> None: + from meshbay_node import platform as _plat + if sys.platform != "win32": + print("autostart is Windows-only — elsewhere use " + "'systemctl --user enable --now meshbay-node'.") + sys.exit(1) + sub = args.subcommand or "status" + if sub == "install": + _plat.autostart_install() + print("Installed the Startup launcher — meshbay-node starts at " + "each sign-in (no window, no admin).") + print("Start it now with: meshbay-node autostart start") + elif sub == "remove": + _plat.autostart_remove() + print("Removed the Startup launcher.") + elif sub == "start": + try: + _plat.autostart_run() + except RuntimeError as e: + print(f"Could not start: {e}") + sys.exit(1) + print("started") + elif sub == "stop": + _plat.autostart_end() + print("stopped") + elif sub == "status": + st = _plat.autostart_status() + if st["installed"]: + print("autostart installed — runs meshbay-node at sign-in") + else: + print("autostart not installed — meshbay-node autostart install") + else: + print("autostart: install | remove | start | stop | status") + sys.exit(1) + return + + +def service(args) -> None: + from meshbay_node import platform as _plat + if sys.platform != "win32": + print("service mode is Windows-only — elsewhere use " + "'systemctl --user enable --now meshbay-node'.") + sys.exit(1) + sub = args.subcommand or "status" + if sub == "install": + try: + _plat.service_install() + except RuntimeError as e: + print(f"Could not install: {e}") + if "denied" in str(e).lower(): + print("Run this from an elevated (Administrator) prompt.") + sys.exit(1) + print(f"Registered the {_plat.TASK_NAME!r} scheduled task — it starts " + "meshbay-node at boot, as this user, whether or not you have " + "signed in yet (no password stored).") + print("Start it now with: meshbay-node service start") + elif sub == "remove": + _plat.service_remove() + print(f"Removed the {_plat.TASK_NAME!r} scheduled task.") + elif sub == "start": + _plat.service_run() + print("started") + elif sub == "stop": + _plat.service_end() + print("stopped") + elif sub == "status": + st = _plat.service_status() + if st["installed"]: + print(f"service installed — {st['state'] or 'unknown state'}") + else: + print("service not installed — meshbay-node service install " + "(needs an elevated prompt)") + else: + print("service: install | remove | start | stop | status") + sys.exit(1) + return -- cgit v1.2.3