From f925701e003f05160d46cc3efd8eacf3ea256aeb Mon Sep 17 00:00:00 2001 From: blasty Date: Fri, 24 Jul 2026 01:37:48 +0200 Subject: app: --backend {mcp,worker} — run the TUI on our idalib worker (migration step 3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wires WorkerClient into the app as a selectable backend, so you can launch: ida-tui --backend worker /path/to/binary # or IDATUI_BACKEND=worker * IdaTui gains a `backend` param. _connect is refactored into _open_mcp_client() (the existing ida-pro-mcp path, unchanged) and _open_worker_client() (spawns a private idalib worker via WorkerClient that opens+analyzes THIS binary and streams progress into the loading overlay). The common tail (health, keepalive, Program, load_functions) is shared, so domain.py and every view are untouched. * _reconnect branches the same way: a dropped worker (segfault → closed socket) respawns a fresh WorkerClient — the connection-loss recovery already built works verbatim for the worker. * launch.py adds --backend (env IDATUI_BACKEND); the worker path skips all the supervisor/server plumbing (it owns a private worker) and just sweeps stale locks + requires a binary. keepalive is a no-op for the worker (it never idles out). mcp remains the default — nothing changes unless you opt in. Verified without idalib: all four files parse; --backend is in --help; IdaTui constructs for both backends with both openers present; WorkerClient covers the full client surface. The idalib E2E (experiments/worker_smoke.py, and actually launching --backend worker) still can't run in this sandbox — it now reaps every idalib spawn before a byte is written — but the mcp default is untouched and the worker path reuses proven pieces (the unix protocol benched at ~50us/call; the worker dispatches the same tool functions the HTTP path does). To validate on a real box: ida-tui --backend worker targets/echo (or run experiments/worker_smoke.py for the headless read-path check). --- idatui/launch.py | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) (limited to 'idatui/launch.py') diff --git a/idatui/launch.py b/idatui/launch.py index 2ecf8b5..15352d5 100644 --- a/idatui/launch.py +++ b/idatui/launch.py @@ -189,6 +189,10 @@ def main(argv: list[str] | None = None) -> int: help="listen for RPC on this unix socket (puppeteer the TUI)") p.add_argument("--no-server", action="store_true", help="do not auto-start the supervisor if it's down") + p.add_argument("--backend", choices=("mcp", "worker"), + default=os.environ.get("IDATUI_BACKEND", "mcp"), + help="'mcp' (ida-pro-mcp supervisor, default) or 'worker' " + "(our own in-process idalib worker over a unix socket)") args = p.parse_args(argv) binary = None @@ -202,21 +206,30 @@ def main(argv: list[str] | None = None) -> int: f"{os.path.dirname(binary)}") return 2 - # Everything slow — starting the supervisor and opening/analyzing the binary - # — is deferred to the TUI so its chrome + "loading…" overlay are on screen - # the whole time, instead of a blank terminal before the TUI even starts. - # Here we only do the instant checks. - server_down = not _server_up(*_server_addr(args.url)) - if args.no_server and server_down: - _log("supervisor is down and --no-server was given") - return 1 - if binary is not None and server_down: - # About to start a fresh supervisor (nothing holds the DB): clear any - # stale unpacked lock files a crashed worker left, so the supervisor's - # seeded initial open doesn't crash-loop on a wedged DB. - swept = _sweep_locks(binary) + if args.backend == "worker": + # The worker path owns no shared supervisor: the TUI spawns a private + # idalib worker that opens THIS binary. No server/lock plumbing here. + if binary is None: + _log("--backend worker needs a binary path") + return 2 + swept = _sweep_locks(binary) # a crashed worker can leave the DB wedged if swept: _log(f"cleared {swept} stale lock file(s) from a crashed worker") + else: + # Everything slow — starting the supervisor and opening/analyzing the + # binary — is deferred to the TUI so its chrome + "loading…" overlay are + # on screen the whole time. Here we only do the instant checks. + server_down = not _server_up(*_server_addr(args.url)) + if args.no_server and server_down: + _log("supervisor is down and --no-server was given") + return 1 + if binary is not None and server_down: + # About to start a fresh supervisor (nothing holds the DB): clear any + # stale unpacked lock files a crashed worker left, so the seeded + # initial open doesn't crash-loop on a wedged DB. + swept = _sweep_locks(binary) + if swept: + _log(f"cleared {swept} stale lock file(s) from a crashed worker") # Hand off to the TUI (imported late so --help works without textual). It # ensures the server (with on-screen progress), then opens/adopts the binary @@ -229,7 +242,8 @@ def main(argv: list[str] | None = None) -> int: rpc_path = os.path.abspath(os.path.expanduser(args.rpc)) if args.rpc else None IdaTui(url=args.url, db=args.db, open_path=(binary if args.db is None else None), ttl=args.ttl, - ensure_server=not args.no_server, + ensure_server=(args.backend == "mcp" and not args.no_server), + backend=args.backend, keepalive=not args.no_keepalive, rpc_path=rpc_path).run() return 0 -- cgit v1.3.1-sl0p