From 7bffa84be9970d17133cd5feabda0bb826949d4b Mon Sep 17 00:00:00 2001 From: YOUR_GITHUB_USERNAME Date: Sun, 14 Jun 2026 08:09:16 +0300 Subject: [PATCH] feat: add headed mode and custom display support to CLI --- cli.py | 8 ++++++++ src/daemon.py | 24 +++++++++++++++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/cli.py b/cli.py index 7ea0466..68eae14 100755 --- a/cli.py +++ b/cli.py @@ -44,6 +44,8 @@ async def cmd_start(args): logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") daemon = Daemon(browser=args.browser, + headed=args.headed, + display=args.display, idle_timeout=getattr(args, 'idle_timeout', None), proxy=getattr(args, 'proxy', None)) await daemon.run() @@ -51,6 +53,10 @@ async def cmd_start(args): # Launch daemon as subprocess cmd = [sys.executable, "-m", "src.daemon", "start", "--browser", args.browser] + if args.headed: + cmd += ["--headed"] + if args.display: + cmd += ["--display", args.display] if getattr(args, 'idle_timeout', None): cmd += ["--idle-timeout", str(args.idle_timeout)] if getattr(args, 'proxy', None): @@ -448,6 +454,8 @@ def main(): # ── Daemon lifecycle ── p = sub.add_parser("start", help="Start browser daemon", parents=[common]) p.add_argument("--foreground", "-f", action="store_true") + p.add_argument("--headed", action="store_true", help="Run in headed mode (visible window)") + p.add_argument("--display", help="X11 display to use (default: :0 for headed, :99 for headless)") p.add_argument("--idle-timeout", type=int, default=None, help="Auto-shutdown after N seconds of inactivity") p.add_argument("--proxy", default=None, diff --git a/src/daemon.py b/src/daemon.py index fd9b391..4bd58dd 100644 --- a/src/daemon.py +++ b/src/daemon.py @@ -49,9 +49,11 @@ def _draw_cursor_overlay(path, cx, cy): class Daemon: """Background daemon managing browser and socket server.""" - def __init__(self, browser="firefox", session_file=None, - idle_timeout=None, proxy=None): + def __init__(self, browser="firefox", headed=False, display=None, + session_file=None, idle_timeout=None, proxy=None): self._browser_type = browser + self._headed = headed + self._display = display self._session_file = session_file self._idle_timeout = idle_timeout # Auto-shutdown after N seconds idle self._proxy = proxy # SOCKS5/HTTP proxy (e.g. socks5://127.0.0.1:1080) @@ -88,8 +90,15 @@ async def run(self): with open(_user_js, "w") as f: f.write(_cert_pref + "\n") from .pilot import Pilot + # Default display: :0 for headed, :99 for headless + display = self._display + if not display: + display = ":0" if self._headed else ":99" + self.pilot = Pilot( browser=self._browser_type, + display=display, + headless_xvfb=not self._headed, session_file=self._session_file, user_data_dir=FIREFOX_PROFILE_DIR, proxy=self._proxy, @@ -5308,6 +5317,8 @@ def main(): parser.add_argument("--session", "-s", default=None) parser.add_argument("--foreground", "-f", action="store_true", help="Run in foreground (don't daemonize)") + parser.add_argument("--headed", action="store_true") + parser.add_argument("--display", default=None) parser.add_argument("--idle-timeout", type=int, default=None, help="Auto-shutdown after N seconds idle (0=disabled)") parser.add_argument("--proxy", default=None, @@ -5318,7 +5329,8 @@ def main(): # Run in foreground (used internally) logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") - daemon = Daemon(browser=args.browser, session_file=args.session, + daemon = Daemon(browser=args.browser, headed=args.headed, + display=args.display, session_file=args.session, idle_timeout=args.idle_timeout, proxy=args.proxy) asyncio.run(daemon.run()) @@ -5342,7 +5354,8 @@ def main(): if args.foreground: logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") - daemon = Daemon(browser=args.browser, session_file=args.session, + daemon = Daemon(browser=args.browser, headed=args.headed, + display=args.display, session_file=args.session, idle_timeout=args.idle_timeout, proxy=args.proxy) asyncio.run(daemon.run()) else: @@ -5363,7 +5376,8 @@ def main(): format="%(asctime)s %(levelname)s %(message)s", handlers=[logging.StreamHandler()], ) - daemon = Daemon(browser=args.browser, session_file=args.session, + daemon = Daemon(browser=args.browser, headed=args.headed, + display=args.display, session_file=args.session, idle_timeout=args.idle_timeout, proxy=args.proxy) asyncio.run(daemon.run())