quash-mcp 0.2.0__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of quash-mcp might be problematic. Click here for more details.
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/PKG-INFO +2 -1
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/pyproject.toml +2 -1
- quash_mcp-0.2.1/quash_mcp/device/__init__.py +34 -0
- quash_mcp-0.2.1/quash_mcp/device/adb_tools.py +108 -0
- quash_mcp-0.2.1/quash_mcp/device/portal.py +163 -0
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/quash_mcp/tools/build.py +38 -25
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/quash_mcp/tools/connect.py +2 -2
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/.gitignore +0 -0
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/README.md +0 -0
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/SETUP_CLAUDE_CODE.md +0 -0
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/quash_mcp/__init__.py +0 -0
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/quash_mcp/__main__.py +0 -0
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/quash_mcp/backend_client.py +0 -0
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/quash_mcp/server.py +0 -0
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/quash_mcp/state.py +0 -0
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/quash_mcp/tools/__init__.py +0 -0
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/quash_mcp/tools/build_old.py +0 -0
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/quash_mcp/tools/configure.py +0 -0
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/quash_mcp/tools/execute.py +0 -0
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/quash_mcp/tools/runsuite.py +0 -0
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/quash_mcp/tools/usage.py +0 -0
- {quash_mcp-0.2.0 → quash_mcp-0.2.1}/test_backend_integration.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quash-mcp
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Model Context Protocol server for Quash - AI-powered mobile automation agent
|
|
5
5
|
Project-URL: Homepage, https://quashbugs.com
|
|
6
6
|
Project-URL: Repository, https://github.com/quash/quash-mcp
|
|
@@ -24,6 +24,7 @@ Requires-Dist: httpx>=0.27.0
|
|
|
24
24
|
Requires-Dist: mcp>=0.9.0
|
|
25
25
|
Requires-Dist: pydantic>=2.0.0
|
|
26
26
|
Requires-Dist: python-dotenv>=1.0.0
|
|
27
|
+
Requires-Dist: requests>=2.31.0
|
|
27
28
|
Requires-Dist: rich>=13.0.0
|
|
28
29
|
Provides-Extra: dev
|
|
29
30
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "quash-mcp"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.1"
|
|
4
4
|
description = "Model Context Protocol server for Quash - AI-powered mobile automation agent"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = {text = "MIT"}
|
|
@@ -23,6 +23,7 @@ dependencies = [
|
|
|
23
23
|
"rich>=13.0.0",
|
|
24
24
|
"pydantic>=2.0.0",
|
|
25
25
|
"python-dotenv>=1.0.0",
|
|
26
|
+
"requests>=2.31.0",
|
|
26
27
|
"adbutils==2.10.0",
|
|
27
28
|
"apkutils==2.0.0",
|
|
28
29
|
]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Device management module for Quash MCP.
|
|
3
|
+
|
|
4
|
+
This module contains utilities for managing Android devices, including:
|
|
5
|
+
- Portal APK installation and setup
|
|
6
|
+
- ADB device communication
|
|
7
|
+
- Accessibility service management
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .portal import (
|
|
11
|
+
download_portal_apk,
|
|
12
|
+
get_local_portal_apk,
|
|
13
|
+
use_portal_apk,
|
|
14
|
+
enable_portal_accessibility,
|
|
15
|
+
check_portal_accessibility,
|
|
16
|
+
ping_portal,
|
|
17
|
+
ping_portal_content,
|
|
18
|
+
ping_portal_tcp,
|
|
19
|
+
PORTAL_PACKAGE_NAME,
|
|
20
|
+
A11Y_SERVICE_NAME,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"download_portal_apk",
|
|
25
|
+
"get_local_portal_apk",
|
|
26
|
+
"use_portal_apk",
|
|
27
|
+
"enable_portal_accessibility",
|
|
28
|
+
"check_portal_accessibility",
|
|
29
|
+
"ping_portal",
|
|
30
|
+
"ping_portal_content",
|
|
31
|
+
"ping_portal_tcp",
|
|
32
|
+
"PORTAL_PACKAGE_NAME",
|
|
33
|
+
"A11Y_SERVICE_NAME",
|
|
34
|
+
]
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ADB Tools - Basic Android device communication wrapper.
|
|
3
|
+
Simplified version for device management without agent-specific functionality.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
from typing import Optional
|
|
8
|
+
from adbutils import adb
|
|
9
|
+
import requests
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger("quash-device")
|
|
12
|
+
PORTAL_DEFAULT_TCP_PORT = 8080
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AdbTools:
|
|
16
|
+
"""Basic ADB device communication wrapper."""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
serial: str | None = None,
|
|
21
|
+
use_tcp: bool = False,
|
|
22
|
+
remote_tcp_port: int = PORTAL_DEFAULT_TCP_PORT,
|
|
23
|
+
) -> None:
|
|
24
|
+
"""Initialize the AdbTools instance.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
serial: Device serial number
|
|
28
|
+
use_tcp: Whether to use TCP communication (default: False)
|
|
29
|
+
remote_tcp_port: TCP port for communication (default: 8080)
|
|
30
|
+
"""
|
|
31
|
+
self.device = adb.device(serial=serial)
|
|
32
|
+
self.use_tcp = use_tcp
|
|
33
|
+
self.remote_tcp_port = remote_tcp_port
|
|
34
|
+
self.tcp_forwarded = False
|
|
35
|
+
|
|
36
|
+
# Set up TCP forwarding if requested
|
|
37
|
+
if self.use_tcp:
|
|
38
|
+
self.setup_tcp_forward()
|
|
39
|
+
|
|
40
|
+
def setup_tcp_forward(self) -> bool:
|
|
41
|
+
"""
|
|
42
|
+
Set up ADB TCP port forwarding for communication with the portal app.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
bool: True if forwarding was set up successfully, False otherwise
|
|
46
|
+
"""
|
|
47
|
+
try:
|
|
48
|
+
logger.debug(
|
|
49
|
+
f"Setting up TCP port forwarding for port tcp:{self.remote_tcp_port} on device {self.device.serial}"
|
|
50
|
+
)
|
|
51
|
+
# Use adb forward command to set up port forwarding
|
|
52
|
+
self.local_tcp_port = self.device.forward_port(self.remote_tcp_port)
|
|
53
|
+
self.tcp_base_url = f"http://localhost:{self.local_tcp_port}"
|
|
54
|
+
logger.debug(
|
|
55
|
+
f"TCP port forwarding set up successfully to {self.tcp_base_url}"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Test the connection with a ping
|
|
59
|
+
try:
|
|
60
|
+
response = requests.get(f"{self.tcp_base_url}/ping", timeout=5)
|
|
61
|
+
if response.status_code == 200:
|
|
62
|
+
logger.debug("TCP connection test successful")
|
|
63
|
+
self.tcp_forwarded = True
|
|
64
|
+
return True
|
|
65
|
+
else:
|
|
66
|
+
logger.warning(
|
|
67
|
+
f"TCP connection test failed with status: {response.status_code}"
|
|
68
|
+
)
|
|
69
|
+
return False
|
|
70
|
+
except requests.exceptions.RequestException as e:
|
|
71
|
+
logger.warning(f"TCP connection test failed: {e}")
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
except Exception as e:
|
|
75
|
+
logger.error(f"Failed to set up TCP port forwarding: {e}")
|
|
76
|
+
self.tcp_forwarded = False
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
def teardown_tcp_forward(self) -> bool:
|
|
80
|
+
"""
|
|
81
|
+
Remove ADB TCP port forwarding.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
bool: True if forwarding was removed successfully, False otherwise
|
|
85
|
+
"""
|
|
86
|
+
try:
|
|
87
|
+
if self.tcp_forwarded:
|
|
88
|
+
logger.debug(
|
|
89
|
+
f"Removing TCP port forwarding for port {self.local_tcp_port}"
|
|
90
|
+
)
|
|
91
|
+
# remove forwarding
|
|
92
|
+
cmd = f"killforward:tcp:{self.local_tcp_port}"
|
|
93
|
+
logger.debug(f"Removing TCP port forwarding: {cmd}")
|
|
94
|
+
c = self.device.open_transport(cmd)
|
|
95
|
+
c.close()
|
|
96
|
+
|
|
97
|
+
self.tcp_forwarded = False
|
|
98
|
+
logger.debug(f"TCP port forwarding removed")
|
|
99
|
+
return True
|
|
100
|
+
return True
|
|
101
|
+
except Exception as e:
|
|
102
|
+
logger.error(f"Failed to remove TCP port forwarding: {e}")
|
|
103
|
+
return False
|
|
104
|
+
|
|
105
|
+
def __del__(self):
|
|
106
|
+
"""Cleanup when the object is destroyed."""
|
|
107
|
+
if hasattr(self, "tcp_forwarded") and self.tcp_forwarded:
|
|
108
|
+
self.teardown_tcp_forward()
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import contextlib
|
|
3
|
+
import tempfile
|
|
4
|
+
import requests
|
|
5
|
+
from adbutils import adb, AdbDevice
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
|
|
8
|
+
ASSET_NAME = "mahoraga-portal"
|
|
9
|
+
APK_DOWNLOAD_URL = "https://storage.googleapis.com/misc_quash_static/mahoraga-portal-v0.1.apk"
|
|
10
|
+
|
|
11
|
+
PORTAL_PACKAGE_NAME = "com.mahoraga.portal"
|
|
12
|
+
A11Y_SERVICE_NAME = (
|
|
13
|
+
f"{PORTAL_PACKAGE_NAME}/com.mahoraga.portal.MahoragaAccessibilityService"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def download_portal_apk(debug: bool = False) -> str:
|
|
18
|
+
"""Download the Mahoraga Portal APK from cloud storage."""
|
|
19
|
+
console = Console()
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
console.print("📥 Downloading Quash Portal APK...")
|
|
23
|
+
if debug:
|
|
24
|
+
console.print(f"Download URL: {APK_DOWNLOAD_URL}")
|
|
25
|
+
|
|
26
|
+
response = requests.get(APK_DOWNLOAD_URL, stream=True)
|
|
27
|
+
response.raise_for_status()
|
|
28
|
+
|
|
29
|
+
# Create temporary file
|
|
30
|
+
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.apk')
|
|
31
|
+
|
|
32
|
+
# Download with progress
|
|
33
|
+
total_size = int(response.headers.get('content-length', 0))
|
|
34
|
+
downloaded = 0
|
|
35
|
+
|
|
36
|
+
for chunk in response.iter_content(chunk_size=8192):
|
|
37
|
+
if chunk:
|
|
38
|
+
temp_file.write(chunk)
|
|
39
|
+
downloaded += len(chunk)
|
|
40
|
+
if total_size > 0:
|
|
41
|
+
progress = (downloaded / total_size) * 100
|
|
42
|
+
console.print(f"\rProgress: {progress:.1f}%", end="")
|
|
43
|
+
|
|
44
|
+
temp_file.close()
|
|
45
|
+
console.print(f"\n✅ Downloaded APK to {temp_file.name}")
|
|
46
|
+
return temp_file.name
|
|
47
|
+
|
|
48
|
+
except requests.RequestException as e:
|
|
49
|
+
raise Exception(f"Failed to download Portal APK: {e}")
|
|
50
|
+
except Exception as e:
|
|
51
|
+
raise Exception(f"Error downloading Portal APK: {e}")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_local_portal_apk(apk_path: str = None):
|
|
55
|
+
"""Get the path to a local portal APK file."""
|
|
56
|
+
if apk_path and os.path.exists(apk_path):
|
|
57
|
+
return apk_path
|
|
58
|
+
|
|
59
|
+
# Look for APK in common locations
|
|
60
|
+
common_paths = [
|
|
61
|
+
f"{ASSET_NAME}.apk",
|
|
62
|
+
f"./assets/{ASSET_NAME}.apk",
|
|
63
|
+
f"./portal/{ASSET_NAME}.apk"
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
for path in common_paths:
|
|
67
|
+
if os.path.exists(path):
|
|
68
|
+
return path
|
|
69
|
+
|
|
70
|
+
# If no local APK found, download it
|
|
71
|
+
return download_portal_apk()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@contextlib.contextmanager
|
|
75
|
+
def use_portal_apk(apk_path: str = None, debug: bool = False):
|
|
76
|
+
console = Console()
|
|
77
|
+
temp_file = None
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
local_apk_path = get_local_portal_apk(apk_path)
|
|
81
|
+
console.print(f"📱 Using Portal APK: [bold]{os.path.basename(local_apk_path)}[/bold]")
|
|
82
|
+
if debug:
|
|
83
|
+
console.print(f"APK Path: {local_apk_path}")
|
|
84
|
+
|
|
85
|
+
# Track if this is a temp file we downloaded
|
|
86
|
+
if local_apk_path.startswith(tempfile.gettempdir()):
|
|
87
|
+
temp_file = local_apk_path
|
|
88
|
+
|
|
89
|
+
yield local_apk_path
|
|
90
|
+
except Exception as e:
|
|
91
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
92
|
+
raise
|
|
93
|
+
finally:
|
|
94
|
+
# Clean up downloaded temp file
|
|
95
|
+
if temp_file and os.path.exists(temp_file):
|
|
96
|
+
try:
|
|
97
|
+
os.unlink(temp_file)
|
|
98
|
+
if debug:
|
|
99
|
+
console.print(f"🗑️ Cleaned up temp file: {temp_file}")
|
|
100
|
+
except Exception:
|
|
101
|
+
pass # Ignore cleanup errors
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def enable_portal_accessibility(device: AdbDevice):
|
|
105
|
+
device.shell(
|
|
106
|
+
f"settings put secure enabled_accessibility_services {A11Y_SERVICE_NAME}"
|
|
107
|
+
)
|
|
108
|
+
device.shell("settings put secure accessibility_enabled 1")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def check_portal_accessibility(device: AdbDevice, debug: bool = False) -> bool:
|
|
112
|
+
a11y_services = device.shell("settings get secure enabled_accessibility_services")
|
|
113
|
+
if not A11Y_SERVICE_NAME in a11y_services:
|
|
114
|
+
if debug:
|
|
115
|
+
print(a11y_services)
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
a11y_enabled = device.shell("settings get secure accessibility_enabled")
|
|
119
|
+
if a11y_enabled != "1":
|
|
120
|
+
if debug:
|
|
121
|
+
print(a11y_enabled)
|
|
122
|
+
return False
|
|
123
|
+
|
|
124
|
+
return True
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def ping_portal(device: AdbDevice, debug: bool = False):
|
|
128
|
+
"""
|
|
129
|
+
Ping the Quash Portal to check if it is installed and accessible.
|
|
130
|
+
"""
|
|
131
|
+
try:
|
|
132
|
+
packages = device.list_packages()
|
|
133
|
+
except Exception as e:
|
|
134
|
+
raise Exception(f"Failed to list packages: {e}")
|
|
135
|
+
|
|
136
|
+
if not PORTAL_PACKAGE_NAME in packages:
|
|
137
|
+
if debug:
|
|
138
|
+
print(packages)
|
|
139
|
+
raise Exception("Portal is not installed on the device")
|
|
140
|
+
|
|
141
|
+
if not check_portal_accessibility(device, debug):
|
|
142
|
+
device.shell("am start -a android.settings.ACCESSIBILITY_SETTINGS")
|
|
143
|
+
raise Exception(
|
|
144
|
+
"Quash Portal is not enabled as an accessibility service on the device"
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def ping_portal_content(device: AdbDevice, debug: bool = False):
|
|
149
|
+
try:
|
|
150
|
+
state = device.shell("content query --uri content://com.mahoraga.portal/state")
|
|
151
|
+
if not "Row: 0 result=" in state:
|
|
152
|
+
raise Exception("Failed to get state from Quash Portal")
|
|
153
|
+
except Exception as e:
|
|
154
|
+
raise Exception(f"Quash Portal is not reachable: {e}")
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def ping_portal_tcp(device: AdbDevice, debug: bool = False):
|
|
158
|
+
"""Check TCP forwarding to portal."""
|
|
159
|
+
from .adb_tools import AdbTools
|
|
160
|
+
try:
|
|
161
|
+
tools = AdbTools(serial=device.serial, use_tcp=True)
|
|
162
|
+
except Exception as e:
|
|
163
|
+
raise Exception(f"Failed to setup TCP forwarding: {e}")
|
|
@@ -557,7 +557,7 @@ class DependencyChecker:
|
|
|
557
557
|
def check_portal(self) -> Tuple[bool, str, str]:
|
|
558
558
|
"""Check if Portal APK download works"""
|
|
559
559
|
try:
|
|
560
|
-
from
|
|
560
|
+
from quash_mcp.device.portal import download_portal_apk
|
|
561
561
|
return True, "✓ Portal APK download available", None
|
|
562
562
|
except ImportError as e:
|
|
563
563
|
return False, f"✗ Portal module not found: {str(e)}", None
|
|
@@ -644,40 +644,53 @@ async def build() -> Dict[str, Any]:
|
|
|
644
644
|
all_ok = False
|
|
645
645
|
print()
|
|
646
646
|
|
|
647
|
-
# 4. Check Quash Package
|
|
648
|
-
print("4️⃣ Checking Quash package...")
|
|
647
|
+
# 4. Check Quash Package (Optional - only for developers with local source)
|
|
648
|
+
print("4️⃣ Checking Quash package (optional for development)...")
|
|
649
649
|
mhg_ok, mhg_msg, _ = checker.check_mahoraga()
|
|
650
650
|
details["mahoraga"] = mhg_msg
|
|
651
651
|
print(f" {mhg_msg}")
|
|
652
652
|
|
|
653
653
|
mahoraga_just_installed = False
|
|
654
654
|
if not mhg_ok:
|
|
655
|
-
#
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
655
|
+
# Check if we're in development mode (source available)
|
|
656
|
+
project_root = Path(__file__).parent.parent.parent.parent
|
|
657
|
+
mahoraga_src = project_root / "mahoraga"
|
|
658
|
+
|
|
659
|
+
if mahoraga_src.exists() and (mahoraga_src / "pyproject.toml").exists():
|
|
660
|
+
# Only try to install if local source is available (development mode)
|
|
661
|
+
print(" Local Quash source detected. Attempting to install...")
|
|
662
|
+
install_ok, install_msg = checker.install_mahoraga()
|
|
663
|
+
details["mahoraga_install"] = install_msg
|
|
664
|
+
print(f" {install_msg}")
|
|
660
665
|
|
|
661
|
-
|
|
662
|
-
|
|
666
|
+
if install_ok:
|
|
667
|
+
fixes_applied.append("Installed Quash from local source")
|
|
668
|
+
mahoraga_just_installed = True
|
|
663
669
|
else:
|
|
664
|
-
|
|
665
|
-
|
|
670
|
+
# Production mode - skip mahoraga (not needed, device tools in quash_mcp)
|
|
671
|
+
print(" ⏭️ Skipped (not required for end users)")
|
|
672
|
+
details["mahoraga"] = "⏭️ Skipped (device tools included in quash-mcp)"
|
|
666
673
|
print()
|
|
667
674
|
|
|
668
|
-
# 5. Check Quash Version
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
675
|
+
# 5. Check Quash Version (only if installed)
|
|
676
|
+
if mhg_ok or mahoraga_just_installed:
|
|
677
|
+
print("5️⃣ Checking Quash version...")
|
|
678
|
+
ver_ok, ver_msg, ver_fix = checker.check_mahoraga_version()
|
|
679
|
+
details["mahoraga_version"] = ver_msg
|
|
680
|
+
print(f" {ver_msg}")
|
|
681
|
+
|
|
682
|
+
if not ver_ok:
|
|
683
|
+
# Don't fail build for version mismatch - just warn
|
|
684
|
+
if ver_fix:
|
|
685
|
+
fix_msg = f"💡 Consider upgrading: pip install --upgrade mahoraga"
|
|
686
|
+
details["mahoraga_version_fix"] = fix_msg
|
|
687
|
+
print(f" {fix_msg}")
|
|
688
|
+
print()
|
|
689
|
+
else:
|
|
690
|
+
print("5️⃣ Checking Quash version...")
|
|
691
|
+
print(" ⏭️ Skipped (Quash package not installed)")
|
|
692
|
+
details["mahoraga_version"] = "⏭️ Skipped"
|
|
693
|
+
print()
|
|
681
694
|
|
|
682
695
|
# 6. Check Python Dependencies
|
|
683
696
|
# Note: Only check if Quash wasn't just installed, because 'pip install -e'
|
|
@@ -41,7 +41,7 @@ def check_portal_service(serial: str) -> bool:
|
|
|
41
41
|
"""Check if Quash Portal accessibility service is enabled."""
|
|
42
42
|
try:
|
|
43
43
|
from adbutils import adb
|
|
44
|
-
from
|
|
44
|
+
from quash_mcp.device.portal import ping_portal
|
|
45
45
|
|
|
46
46
|
device = adb.device(serial)
|
|
47
47
|
ping_portal(device, debug=False)
|
|
@@ -54,7 +54,7 @@ def setup_portal(serial: str) -> tuple[bool, str]:
|
|
|
54
54
|
"""Setup Quash Portal on the device."""
|
|
55
55
|
try:
|
|
56
56
|
from adbutils import adb
|
|
57
|
-
from
|
|
57
|
+
from quash_mcp.device.portal import use_portal_apk, enable_portal_accessibility
|
|
58
58
|
|
|
59
59
|
device = adb.device(serial)
|
|
60
60
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|