From 16d710a495faa81d26a866b14fec6ce456a63e44 Mon Sep 17 00:00:00 2001 From: gambit36 <35210554+gambit36@users.noreply.github.com> Date: Fri, 13 Mar 2026 02:16:18 +0800 Subject: [PATCH] Fix app PID selection to avoid helper processes --- examples/print_app_tree.py | 16 +++++----- mlx_use/controller/service.py | 56 +++++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 14 deletions(-) diff --git a/examples/print_app_tree.py b/examples/print_app_tree.py index 1633ade..83ffe4e 100644 --- a/examples/print_app_tree.py +++ b/examples/print_app_tree.py @@ -8,7 +8,7 @@ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from mlx_use.mac.tree import MacUITreeBuilder -from mlx_use.controller.service import Controller +from mlx_use.controller.service import Controller, _find_best_running_app from mlx_use.controller.views import OpenAppAction from mlx_use.agent.views import ActionModel @@ -38,13 +38,13 @@ async def print_app_tree(app_name: str): # Find the app's PID app_pid = None - for app in workspace.runningApplications(): - if app.bundleIdentifier() and bundle_id.lower() in app.bundleIdentifier().lower(): - print(f'Found {formatted_app_name}!') - print(f'Bundle ID: {app.bundleIdentifier()}') - app_pid = app.processIdentifier() - print(f'PID: {app_pid}') - break + best_app = _find_best_running_app(workspace, formatted_app_name) + if best_app is not None: + print(f'Found {formatted_app_name}!') + print(f'Localized name: {best_app.localizedName()}') + print(f'Bundle ID: {best_app.bundleIdentifier()}') + app_pid = best_app.processIdentifier() + print(f'PID: {app_pid}') if not app_pid: print(f'❌ Could not find {formatted_app_name} app') diff --git a/mlx_use/controller/service.py b/mlx_use/controller/service.py index 6c2f222..56c7329 100644 --- a/mlx_use/controller/service.py +++ b/mlx_use/controller/service.py @@ -7,6 +7,50 @@ import Cocoa from playwright.async_api import Page + +def _score_running_app(app, app_name: str) -> int: + """Prefer the real app process over helper/background processes.""" + name = (str(app.localizedName() or '')).lower() + bundle_id = (str(app.bundleIdentifier() or '')).lower() + target = app_name.lower() + score = 0 + + if name == target: + score += 100 + elif target in name: + score += 40 + + if bundle_id == f'com.apple.{target}': + score += 120 + elif bundle_id.endswith(f'.{target}'): + score += 80 + elif target in bundle_id: + score += 20 + + if 'helper' in name or 'helper' in bundle_id: + score -= 200 + if 'platformsupport' in name or 'platformsupport' in bundle_id: + score -= 200 + if 'web content' in name or 'webcontent' in bundle_id: + score -= 120 + + return score + + +def _find_best_running_app(workspace, app_name: str): + apps = list(workspace.runningApplications()) + scored = [] + for app in apps: + try: + scored.append((_score_running_app(app, app_name), app)) + except Exception: + continue + if not scored: + return None + scored.sort(key=lambda item: item[0], reverse=True) + best_score, best_app = scored[0] + return best_app if best_score > 0 else None + from mlx_use.agent.views import ActionModel, ActionResult from mlx_use.controller.registry.service import Registry from mlx_use.controller.views import ( @@ -188,12 +232,12 @@ async def open_app(app_name: str): await asyncio.sleep(1) # Give it a moment to appear in running apps pid = None - for app in workspace.runningApplications(): - if app.bundleIdentifier() and app_name.lower() in app.bundleIdentifier().lower(): - logging.debug(f'Bundle ID: {app.bundleIdentifier()}') - pid = app.processIdentifier() - logging.debug(f'PID: {pid}') - break + best_app = _find_best_running_app(workspace, app_name) + if best_app is not None: + logging.debug(f'Localized name: {best_app.localizedName()}') + logging.debug(f'Bundle ID: {best_app.bundleIdentifier()}') + pid = best_app.processIdentifier() + logging.debug(f'PID: {pid}') # If no PID is found, try to find the app by name with pgrep -i if pid is None: