autoglm-gui 1.4.1__tar.gz → 1.5.0__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.
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/__init__.py +11 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/__main__.py +26 -4
- autoglm_gui-1.5.0/AutoGLM_GUI/actions/__init__.py +6 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/actions/handler.py +196 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/actions/types.py +15 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/adb/__init__.py +53 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/adb/connection.py +323 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/adb/device.py +171 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/adb/input.py +67 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/adb/screenshot.py +11 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/adb/timing.py +167 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/adb_plus/keyboard_installer.py +4 -2
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/adb_plus/screenshot.py +22 -1
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/adb_plus/serial.py +38 -20
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/adb_plus/touch.py +4 -9
- autoglm_gui-1.5.0/AutoGLM_GUI/agents/__init__.py +51 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/agents/events.py +19 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/agents/factory.py +31 -38
- autoglm_gui-1.5.0/AutoGLM_GUI/agents/glm/__init__.py +7 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/agents/glm/agent.py +292 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/agents/glm/message_builder.py +81 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/agents/glm/parser.py +110 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/agents/glm/prompts_en.py +77 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/agents/glm/prompts_zh.py +75 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/agents/mai/__init__.py +28 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/agents/mai/agent.py +405 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/agents/mai/parser.py +254 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/agents/mai/prompts.py +103 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/agents/mai/traj_memory.py +91 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/agents/protocols.py +27 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/agents/stream_runner.py +188 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/api/__init__.py +40 -21
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/api/agents.py +157 -240
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/api/control.py +9 -6
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/api/devices.py +102 -12
- autoglm_gui-1.5.0/AutoGLM_GUI/api/history.py +78 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/api/layered_agent.py +67 -15
- autoglm_gui-1.5.0/AutoGLM_GUI/api/media.py +112 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/api/scheduled_tasks.py +98 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/config.py +81 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/config_manager.py +68 -51
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/device_manager.py +248 -29
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/device_protocol.py +1 -1
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/devices/adb_device.py +5 -10
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/devices/mock_device.py +4 -2
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/devices/remote_device.py +8 -3
- autoglm_gui-1.5.0/AutoGLM_GUI/history_manager.py +164 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/model/__init__.py +5 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/model/message_builder.py +69 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/model/types.py +24 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/models/__init__.py +10 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/models/history.py +96 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/models/scheduled_task.py +71 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/parsers/__init__.py +22 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/parsers/base.py +50 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/parsers/phone_parser.py +58 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/phone_agent_manager.py +62 -396
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/platform_utils.py +26 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/prompt_config.py +15 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/prompts/__init__.py +32 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/scheduler_manager.py +304 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/schemas.py +234 -72
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/scrcpy_stream.py +142 -24
- autoglm_gui-1.5.0/AutoGLM_GUI/socketio_server.py +210 -0
- autoglm_gui-1.4.1/AutoGLM_GUI/static/assets/about-_XNhzQZX.js → autoglm_gui-1.5.0/AutoGLM_GUI/static/assets/about-BQm96DAl.js +1 -1
- autoglm_gui-1.5.0/AutoGLM_GUI/static/assets/alert-dialog-B42XxGPR.js +1 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/static/assets/chat-C0L2gQYG.js +129 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/static/assets/circle-alert-D4rSJh37.js +1 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/static/assets/dialog-DZ78cEcj.js +45 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/static/assets/history-DFBv7TGc.js +1 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/static/assets/index-Bzyv2yQ2.css +1 -0
- autoglm_gui-1.4.1/AutoGLM_GUI/static/assets/index-Cy8TmmHV.js → autoglm_gui-1.5.0/AutoGLM_GUI/static/assets/index-CmZSnDqc.js +1 -1
- autoglm_gui-1.5.0/AutoGLM_GUI/static/assets/index-CssG-3TH.js +11 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/static/assets/label-BCUzE_nm.js +1 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/static/assets/logs-eoFxn5of.js +1 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/static/assets/popover-DLsuV5Sx.js +1 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/static/assets/scheduled-tasks-MyqGJvy_.js +1 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/static/assets/square-pen-zGWYrdfj.js +1 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/static/assets/textarea-BX6y7uM5.js +1 -0
- autoglm_gui-1.5.0/AutoGLM_GUI/static/assets/workflows-CYFs6ssC.js +1 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/static/index.html +2 -2
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/types.py +17 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/PKG-INFO +137 -130
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/README.md +135 -129
- autoglm_gui-1.5.0/phone_agent/config/apps.py +227 -0
- autoglm_gui-1.5.0/phone_agent/config/i18n.py +81 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/pyproject.toml +2 -1
- autoglm_gui-1.4.1/AutoGLM_GUI/agents/__init__.py +0 -20
- autoglm_gui-1.4.1/AutoGLM_GUI/agents/mai_adapter.py +0 -627
- autoglm_gui-1.4.1/AutoGLM_GUI/agents/protocols.py +0 -23
- autoglm_gui-1.4.1/AutoGLM_GUI/api/dual_model.py +0 -317
- autoglm_gui-1.4.1/AutoGLM_GUI/api/media.py +0 -49
- autoglm_gui-1.4.1/AutoGLM_GUI/dual_model/__init__.py +0 -53
- autoglm_gui-1.4.1/AutoGLM_GUI/dual_model/decision_model.py +0 -664
- autoglm_gui-1.4.1/AutoGLM_GUI/dual_model/dual_agent.py +0 -917
- autoglm_gui-1.4.1/AutoGLM_GUI/dual_model/protocols.py +0 -354
- autoglm_gui-1.4.1/AutoGLM_GUI/dual_model/vision_model.py +0 -442
- autoglm_gui-1.4.1/AutoGLM_GUI/mai_ui_adapter/agent_wrapper.py +0 -291
- autoglm_gui-1.4.1/AutoGLM_GUI/phone_agent_patches.py +0 -147
- autoglm_gui-1.4.1/AutoGLM_GUI/socketio_server.py +0 -137
- autoglm_gui-1.4.1/AutoGLM_GUI/static/assets/chat-DwJpiAWf.js +0 -126
- autoglm_gui-1.4.1/AutoGLM_GUI/static/assets/dialog-B3uW4T8V.js +0 -45
- autoglm_gui-1.4.1/AutoGLM_GUI/static/assets/index-Cpv2gSF1.css +0 -1
- autoglm_gui-1.4.1/AutoGLM_GUI/static/assets/index-UYYauTly.js +0 -12
- autoglm_gui-1.4.1/AutoGLM_GUI/static/assets/workflows-Du_de-dt.js +0 -1
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/.gitignore +0 -0
- {autoglm_gui-1.4.1/phone_agent/config → autoglm_gui-1.5.0/AutoGLM_GUI/adb}/apps.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/adb_plus/__init__.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/adb_plus/device.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/adb_plus/ip.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/adb_plus/mdns.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/adb_plus/pair.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/adb_plus/qr_pair.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/adb_plus/version.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/api/health.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/api/mcp.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/api/metrics.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/api/version.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/api/workflows.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/device_adapter.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/devices/__init__.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/exceptions.py +0 -0
- {autoglm_gui-1.4.1/phone_agent/config → autoglm_gui-1.5.0/AutoGLM_GUI}/i18n.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/logger.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/metrics.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/prompts.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/scrcpy_protocol.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/server.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/state.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/static/assets/logo-Cyfm06Ym.png +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/static/assets/worker-D6BRitjy.js +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/static/favicon.ico +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/static/logo-192.png +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/static/logo-512.png +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/version.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/AutoGLM_GUI/workflow_manager.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/LICENSE +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/mai_agent/base.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/mai_agent/mai_grounding_agent.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/mai_agent/mai_naivigation_agent.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/mai_agent/prompt.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/mai_agent/unified_memory.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/mai_agent/utils.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/__init__.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/actions/__init__.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/actions/handler.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/actions/handler_ios.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/adb/__init__.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/adb/connection.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/adb/device.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/adb/input.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/adb/screenshot.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/agent.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/agent_ios.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/config/__init__.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/config/apps_harmonyos.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/config/apps_ios.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/config/prompts.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/config/prompts_en.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/config/prompts_zh.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/config/timing.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/device_factory.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/hdc/__init__.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/hdc/connection.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/hdc/device.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/hdc/input.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/hdc/screenshot.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/model/__init__.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/model/client.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/xctest/__init__.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/xctest/connection.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/xctest/device.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/xctest/input.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/phone_agent/xctest/screenshot.py +0 -0
- {autoglm_gui-1.4.1 → autoglm_gui-1.5.0}/scrcpy-server-v3.3.3 +0 -0
|
@@ -5,6 +5,9 @@ import sys
|
|
|
5
5
|
from functools import wraps
|
|
6
6
|
from importlib import metadata
|
|
7
7
|
|
|
8
|
+
from AutoGLM_GUI.config import AgentConfig, ModelConfig, StepResult
|
|
9
|
+
from AutoGLM_GUI.logger import logger
|
|
10
|
+
|
|
8
11
|
# 修复 Windows 编码问题 - 必须在所有其他导入之前
|
|
9
12
|
if sys.platform == "win32":
|
|
10
13
|
import codecs
|
|
@@ -58,3 +61,11 @@ try:
|
|
|
58
61
|
__version__ = metadata.version("autoglm-gui")
|
|
59
62
|
except metadata.PackageNotFoundError:
|
|
60
63
|
__version__ = "unknown"
|
|
64
|
+
|
|
65
|
+
__all__ = [
|
|
66
|
+
"__version__",
|
|
67
|
+
"ModelConfig",
|
|
68
|
+
"AgentConfig",
|
|
69
|
+
"StepResult",
|
|
70
|
+
"logger",
|
|
71
|
+
]
|
|
@@ -44,19 +44,25 @@ def find_available_port(
|
|
|
44
44
|
)
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
def open_browser(
|
|
47
|
+
def open_browser(
|
|
48
|
+
host: str, port: int, use_ssl: bool = False, delay: float = 1.5
|
|
49
|
+
) -> None:
|
|
48
50
|
"""Open browser after a delay to ensure server is ready.
|
|
49
51
|
|
|
50
52
|
Args:
|
|
51
53
|
host: Server host
|
|
52
54
|
port: Server port
|
|
55
|
+
use_ssl: Whether to use HTTPS
|
|
53
56
|
delay: Delay in seconds before opening browser
|
|
54
57
|
"""
|
|
55
58
|
|
|
56
59
|
def _open():
|
|
57
60
|
time.sleep(delay)
|
|
61
|
+
protocol = "https" if use_ssl else "http"
|
|
58
62
|
url = (
|
|
59
|
-
f"
|
|
63
|
+
f"{protocol}://127.0.0.1:{port}"
|
|
64
|
+
if host == "0.0.0.0"
|
|
65
|
+
else f"{protocol}://{host}:{port}"
|
|
60
66
|
)
|
|
61
67
|
try:
|
|
62
68
|
webbrowser.open(url)
|
|
@@ -125,6 +131,16 @@ def main() -> None:
|
|
|
125
131
|
action="store_true",
|
|
126
132
|
help="Disable file logging",
|
|
127
133
|
)
|
|
134
|
+
parser.add_argument(
|
|
135
|
+
"--ssl-keyfile",
|
|
136
|
+
default=None,
|
|
137
|
+
help="SSL key file path (for HTTPS)",
|
|
138
|
+
)
|
|
139
|
+
parser.add_argument(
|
|
140
|
+
"--ssl-certfile",
|
|
141
|
+
default=None,
|
|
142
|
+
help="SSL certificate file path (for HTTPS)",
|
|
143
|
+
)
|
|
128
144
|
|
|
129
145
|
args = parser.parse_args()
|
|
130
146
|
|
|
@@ -172,6 +188,9 @@ def main() -> None:
|
|
|
172
188
|
# 获取配置来源
|
|
173
189
|
config_source = config_manager.get_config_source()
|
|
174
190
|
|
|
191
|
+
# Determine if SSL is enabled
|
|
192
|
+
use_ssl = args.ssl_keyfile is not None and args.ssl_certfile is not None
|
|
193
|
+
|
|
175
194
|
# Display startup banner
|
|
176
195
|
print()
|
|
177
196
|
print("=" * 50)
|
|
@@ -179,7 +198,8 @@ def main() -> None:
|
|
|
179
198
|
print("=" * 50)
|
|
180
199
|
print(f" Version: {__version__}")
|
|
181
200
|
print()
|
|
182
|
-
|
|
201
|
+
protocol = "https" if use_ssl else "http"
|
|
202
|
+
print(f" Server: {protocol}://{args.host}:{args.port}")
|
|
183
203
|
print()
|
|
184
204
|
print(" Model Configuration:")
|
|
185
205
|
print(f" Source: {config_source.value}")
|
|
@@ -202,13 +222,15 @@ def main() -> None:
|
|
|
202
222
|
|
|
203
223
|
# Open browser automatically unless disabled
|
|
204
224
|
if not args.no_browser:
|
|
205
|
-
open_browser(args.host, args.port)
|
|
225
|
+
open_browser(args.host, args.port, use_ssl=use_ssl)
|
|
206
226
|
|
|
207
227
|
uvicorn.run(
|
|
208
228
|
server.app if not args.reload else "AutoGLM_GUI.server:app",
|
|
209
229
|
host=args.host,
|
|
210
230
|
port=args.port,
|
|
211
231
|
reload=args.reload,
|
|
232
|
+
ssl_keyfile=args.ssl_keyfile,
|
|
233
|
+
ssl_certfile=args.ssl_certfile,
|
|
212
234
|
)
|
|
213
235
|
|
|
214
236
|
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"""Action handler for executing phone operations."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from typing import Any, Callable
|
|
5
|
+
|
|
6
|
+
from AutoGLM_GUI.device_protocol import DeviceProtocol
|
|
7
|
+
|
|
8
|
+
from .types import ActionResult
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ActionHandler:
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
device: DeviceProtocol,
|
|
15
|
+
confirmation_callback: Callable[[str], bool] | None = None,
|
|
16
|
+
takeover_callback: Callable[[str], None] | None = None,
|
|
17
|
+
):
|
|
18
|
+
self.device = device
|
|
19
|
+
self.confirmation_callback = confirmation_callback or self._default_confirmation
|
|
20
|
+
self.takeover_callback = takeover_callback or self._default_takeover
|
|
21
|
+
|
|
22
|
+
def execute(
|
|
23
|
+
self, action: dict[str, Any], screen_width: int, screen_height: int
|
|
24
|
+
) -> ActionResult:
|
|
25
|
+
action_type = action.get("_metadata")
|
|
26
|
+
|
|
27
|
+
if action_type == "finish":
|
|
28
|
+
return ActionResult(
|
|
29
|
+
success=True, should_finish=True, message=action.get("message")
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
if action_type != "do":
|
|
33
|
+
return ActionResult(
|
|
34
|
+
success=False,
|
|
35
|
+
should_finish=True,
|
|
36
|
+
message=f"Unknown action type: {action_type}",
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
action_name = action.get("action")
|
|
40
|
+
if not isinstance(action_name, str) or not action_name:
|
|
41
|
+
return ActionResult(
|
|
42
|
+
success=False,
|
|
43
|
+
should_finish=False,
|
|
44
|
+
message=f"Unknown action: {action_name}",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
handler_method = self._get_handler(action_name)
|
|
48
|
+
|
|
49
|
+
if handler_method is None:
|
|
50
|
+
return ActionResult(
|
|
51
|
+
success=False,
|
|
52
|
+
should_finish=False,
|
|
53
|
+
message=f"Unknown action: {action_name}",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
return handler_method(action, screen_width, screen_height)
|
|
58
|
+
except Exception as e:
|
|
59
|
+
return ActionResult(
|
|
60
|
+
success=False, should_finish=False, message=f"Action failed: {e}"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
def _get_handler(self, action_name: str) -> Callable | None:
|
|
64
|
+
handlers = {
|
|
65
|
+
"Launch": self._handle_launch,
|
|
66
|
+
"Tap": self._handle_tap,
|
|
67
|
+
"Type": self._handle_type,
|
|
68
|
+
"Type_Name": self._handle_type,
|
|
69
|
+
"Swipe": self._handle_swipe,
|
|
70
|
+
"Back": self._handle_back,
|
|
71
|
+
"Home": self._handle_home,
|
|
72
|
+
"Double Tap": self._handle_double_tap,
|
|
73
|
+
"Long Press": self._handle_long_press,
|
|
74
|
+
"Wait": self._handle_wait,
|
|
75
|
+
"Take_over": self._handle_takeover,
|
|
76
|
+
"Note": self._handle_note,
|
|
77
|
+
}
|
|
78
|
+
return handlers.get(action_name)
|
|
79
|
+
|
|
80
|
+
def _convert_relative_to_absolute(
|
|
81
|
+
self, element: list[int], screen_width: int, screen_height: int
|
|
82
|
+
) -> tuple[int, int]:
|
|
83
|
+
x = int(element[0] / 1000 * screen_width)
|
|
84
|
+
y = int(element[1] / 1000 * screen_height)
|
|
85
|
+
return x, y
|
|
86
|
+
|
|
87
|
+
def _handle_launch(self, action: dict, width: int, height: int) -> ActionResult:
|
|
88
|
+
app_name = action.get("app")
|
|
89
|
+
if not app_name:
|
|
90
|
+
return ActionResult(False, False, "No app name specified")
|
|
91
|
+
|
|
92
|
+
success = self.device.launch_app(app_name)
|
|
93
|
+
if success:
|
|
94
|
+
return ActionResult(True, False)
|
|
95
|
+
return ActionResult(False, False, f"App not found: {app_name}")
|
|
96
|
+
|
|
97
|
+
def _handle_tap(self, action: dict, width: int, height: int) -> ActionResult:
|
|
98
|
+
element = action.get("element")
|
|
99
|
+
if not element:
|
|
100
|
+
return ActionResult(False, False, "No element coordinates")
|
|
101
|
+
|
|
102
|
+
x, y = self._convert_relative_to_absolute(element, width, height)
|
|
103
|
+
|
|
104
|
+
if "message" in action:
|
|
105
|
+
if not self.confirmation_callback(action["message"]):
|
|
106
|
+
return ActionResult(
|
|
107
|
+
success=False,
|
|
108
|
+
should_finish=True,
|
|
109
|
+
message="User cancelled sensitive operation",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
self.device.tap(x, y)
|
|
113
|
+
return ActionResult(True, False)
|
|
114
|
+
|
|
115
|
+
def _handle_type(self, action: dict, width: int, height: int) -> ActionResult:
|
|
116
|
+
text = action.get("text", "")
|
|
117
|
+
|
|
118
|
+
original_ime = self.device.detect_and_set_adb_keyboard()
|
|
119
|
+
time.sleep(0.5)
|
|
120
|
+
|
|
121
|
+
self.device.clear_text()
|
|
122
|
+
time.sleep(0.3)
|
|
123
|
+
|
|
124
|
+
self.device.type_text(text)
|
|
125
|
+
time.sleep(0.5)
|
|
126
|
+
|
|
127
|
+
self.device.restore_keyboard(original_ime)
|
|
128
|
+
time.sleep(0.3)
|
|
129
|
+
|
|
130
|
+
return ActionResult(True, False)
|
|
131
|
+
|
|
132
|
+
def _handle_swipe(self, action: dict, width: int, height: int) -> ActionResult:
|
|
133
|
+
start = action.get("start")
|
|
134
|
+
end = action.get("end")
|
|
135
|
+
|
|
136
|
+
if not start or not end:
|
|
137
|
+
return ActionResult(False, False, "Missing swipe coordinates")
|
|
138
|
+
|
|
139
|
+
start_x, start_y = self._convert_relative_to_absolute(start, width, height)
|
|
140
|
+
end_x, end_y = self._convert_relative_to_absolute(end, width, height)
|
|
141
|
+
|
|
142
|
+
self.device.swipe(start_x, start_y, end_x, end_y)
|
|
143
|
+
return ActionResult(True, False)
|
|
144
|
+
|
|
145
|
+
def _handle_back(self, action: dict, width: int, height: int) -> ActionResult:
|
|
146
|
+
self.device.back()
|
|
147
|
+
return ActionResult(True, False)
|
|
148
|
+
|
|
149
|
+
def _handle_home(self, action: dict, width: int, height: int) -> ActionResult:
|
|
150
|
+
self.device.home()
|
|
151
|
+
return ActionResult(True, False)
|
|
152
|
+
|
|
153
|
+
def _handle_double_tap(self, action: dict, width: int, height: int) -> ActionResult:
|
|
154
|
+
element = action.get("element")
|
|
155
|
+
if not element:
|
|
156
|
+
return ActionResult(False, False, "No element coordinates")
|
|
157
|
+
|
|
158
|
+
x, y = self._convert_relative_to_absolute(element, width, height)
|
|
159
|
+
self.device.double_tap(x, y)
|
|
160
|
+
return ActionResult(True, False)
|
|
161
|
+
|
|
162
|
+
def _handle_long_press(self, action: dict, width: int, height: int) -> ActionResult:
|
|
163
|
+
element = action.get("element")
|
|
164
|
+
if not element:
|
|
165
|
+
return ActionResult(False, False, "No element coordinates")
|
|
166
|
+
|
|
167
|
+
x, y = self._convert_relative_to_absolute(element, width, height)
|
|
168
|
+
self.device.long_press(x, y)
|
|
169
|
+
return ActionResult(True, False)
|
|
170
|
+
|
|
171
|
+
def _handle_wait(self, action: dict, width: int, height: int) -> ActionResult:
|
|
172
|
+
duration_str = action.get("duration", "1 seconds")
|
|
173
|
+
try:
|
|
174
|
+
duration = float(duration_str.replace("seconds", "").strip())
|
|
175
|
+
except ValueError:
|
|
176
|
+
duration = 1.0
|
|
177
|
+
|
|
178
|
+
time.sleep(duration)
|
|
179
|
+
return ActionResult(True, False)
|
|
180
|
+
|
|
181
|
+
def _handle_takeover(self, action: dict, width: int, height: int) -> ActionResult:
|
|
182
|
+
message = action.get("message", "User intervention required")
|
|
183
|
+
self.takeover_callback(message)
|
|
184
|
+
return ActionResult(True, False)
|
|
185
|
+
|
|
186
|
+
def _handle_note(self, action: dict, width: int, height: int) -> ActionResult:
|
|
187
|
+
return ActionResult(True, False)
|
|
188
|
+
|
|
189
|
+
@staticmethod
|
|
190
|
+
def _default_confirmation(message: str) -> bool:
|
|
191
|
+
response = input(f"\n⚠️ Confirm action: {message} (y/n): ")
|
|
192
|
+
return response.lower() in ("y", "yes")
|
|
193
|
+
|
|
194
|
+
@staticmethod
|
|
195
|
+
def _default_takeover(message: str) -> None:
|
|
196
|
+
input(f"\n🤚 {message}. Press Enter to continue...")
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Type definitions for actions."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class ActionResult:
|
|
9
|
+
success: bool
|
|
10
|
+
should_finish: bool
|
|
11
|
+
message: str | None = None
|
|
12
|
+
requires_confirmation: bool = False
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
Action = dict[str, Any]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from AutoGLM_GUI.adb.apps import APP_PACKAGES, get_app_name, get_package_name
|
|
2
|
+
from AutoGLM_GUI.adb.connection import (
|
|
3
|
+
ADBConnection,
|
|
4
|
+
ConnectionType,
|
|
5
|
+
DeviceInfo,
|
|
6
|
+
list_devices,
|
|
7
|
+
quick_connect,
|
|
8
|
+
)
|
|
9
|
+
from AutoGLM_GUI.adb.device import (
|
|
10
|
+
back,
|
|
11
|
+
double_tap,
|
|
12
|
+
get_current_app,
|
|
13
|
+
home,
|
|
14
|
+
launch_app,
|
|
15
|
+
long_press,
|
|
16
|
+
swipe,
|
|
17
|
+
tap,
|
|
18
|
+
)
|
|
19
|
+
from AutoGLM_GUI.adb.input import (
|
|
20
|
+
clear_text,
|
|
21
|
+
detect_and_set_adb_keyboard,
|
|
22
|
+
restore_keyboard,
|
|
23
|
+
type_text,
|
|
24
|
+
)
|
|
25
|
+
from AutoGLM_GUI.adb.screenshot import Screenshot, get_screenshot
|
|
26
|
+
from AutoGLM_GUI.adb.timing import TIMING_CONFIG, TimingConfig
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"ADBConnection",
|
|
30
|
+
"ConnectionType",
|
|
31
|
+
"DeviceInfo",
|
|
32
|
+
"list_devices",
|
|
33
|
+
"quick_connect",
|
|
34
|
+
"TIMING_CONFIG",
|
|
35
|
+
"TimingConfig",
|
|
36
|
+
"APP_PACKAGES",
|
|
37
|
+
"get_package_name",
|
|
38
|
+
"get_app_name",
|
|
39
|
+
"tap",
|
|
40
|
+
"double_tap",
|
|
41
|
+
"long_press",
|
|
42
|
+
"swipe",
|
|
43
|
+
"back",
|
|
44
|
+
"home",
|
|
45
|
+
"launch_app",
|
|
46
|
+
"get_current_app",
|
|
47
|
+
"type_text",
|
|
48
|
+
"clear_text",
|
|
49
|
+
"detect_and_set_adb_keyboard",
|
|
50
|
+
"restore_keyboard",
|
|
51
|
+
"Screenshot",
|
|
52
|
+
"get_screenshot",
|
|
53
|
+
]
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
"""ADB connection management for local and remote devices."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
import time
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from enum import Enum
|
|
7
|
+
|
|
8
|
+
from AutoGLM_GUI.adb.timing import TIMING_CONFIG
|
|
9
|
+
from AutoGLM_GUI.adb_plus.ip import get_wifi_ip
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ConnectionType(Enum):
|
|
13
|
+
"""Type of ADB connection."""
|
|
14
|
+
|
|
15
|
+
USB = "usb"
|
|
16
|
+
WIFI = "wifi"
|
|
17
|
+
REMOTE = "remote"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class DeviceInfo:
|
|
22
|
+
"""Information about a connected device."""
|
|
23
|
+
|
|
24
|
+
device_id: str
|
|
25
|
+
status: str
|
|
26
|
+
connection_type: ConnectionType
|
|
27
|
+
model: str | None = None
|
|
28
|
+
android_version: str | None = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ADBConnection:
|
|
32
|
+
"""
|
|
33
|
+
Manages ADB connections to Android devices.
|
|
34
|
+
|
|
35
|
+
Supports USB, WiFi, and remote TCP/IP connections.
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
>>> conn = ADBConnection()
|
|
39
|
+
>>> # Connect to remote device
|
|
40
|
+
>>> conn.connect("192.168.1.100:5555")
|
|
41
|
+
>>> # List devices
|
|
42
|
+
>>> devices = conn.list_devices()
|
|
43
|
+
>>> # Disconnect
|
|
44
|
+
>>> conn.disconnect("192.168.1.100:5555")
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self, adb_path: str = "adb"):
|
|
48
|
+
"""
|
|
49
|
+
Initialize ADB connection manager.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
adb_path: Path to ADB executable.
|
|
53
|
+
"""
|
|
54
|
+
self.adb_path = adb_path
|
|
55
|
+
|
|
56
|
+
def connect(self, address: str, timeout: int = 10) -> tuple[bool, str]:
|
|
57
|
+
"""
|
|
58
|
+
Connect to a remote device via TCP/IP.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
address: Device address in format "host:port" (e.g., "192.168.1.100:5555").
|
|
62
|
+
timeout: Connection timeout in seconds.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Tuple of (success, message).
|
|
66
|
+
|
|
67
|
+
Note:
|
|
68
|
+
The remote device must have TCP/IP debugging enabled.
|
|
69
|
+
On the device, run: adb tcpip 5555
|
|
70
|
+
"""
|
|
71
|
+
# Validate address format
|
|
72
|
+
if ":" not in address:
|
|
73
|
+
address = f"{address}:5555" # Default ADB port
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
result = subprocess.run(
|
|
77
|
+
[self.adb_path, "connect", address],
|
|
78
|
+
capture_output=True,
|
|
79
|
+
text=True,
|
|
80
|
+
timeout=timeout,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
output = result.stdout + result.stderr
|
|
84
|
+
|
|
85
|
+
if "connected" in output.lower():
|
|
86
|
+
return True, f"Connected to {address}"
|
|
87
|
+
elif "already connected" in output.lower():
|
|
88
|
+
return True, f"Already connected to {address}"
|
|
89
|
+
else:
|
|
90
|
+
return False, output.strip()
|
|
91
|
+
|
|
92
|
+
except subprocess.TimeoutExpired:
|
|
93
|
+
return False, f"Connection timeout after {timeout}s"
|
|
94
|
+
except Exception as e:
|
|
95
|
+
return False, f"Connection error: {e}"
|
|
96
|
+
|
|
97
|
+
def disconnect(self, address: str | None = None) -> tuple[bool, str]:
|
|
98
|
+
"""
|
|
99
|
+
Disconnect from a remote device.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
address: Device address to disconnect. If None, disconnects all.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Tuple of (success, message).
|
|
106
|
+
"""
|
|
107
|
+
try:
|
|
108
|
+
cmd = [self.adb_path, "disconnect"]
|
|
109
|
+
if address:
|
|
110
|
+
cmd.append(address)
|
|
111
|
+
|
|
112
|
+
result = subprocess.run(
|
|
113
|
+
cmd, capture_output=True, text=True, encoding="utf-8", timeout=5
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
output = result.stdout + result.stderr
|
|
117
|
+
return True, output.strip() or "Disconnected"
|
|
118
|
+
|
|
119
|
+
except Exception as e:
|
|
120
|
+
return False, f"Disconnect error: {e}"
|
|
121
|
+
|
|
122
|
+
def list_devices(self) -> list[DeviceInfo]:
|
|
123
|
+
"""
|
|
124
|
+
List all connected devices.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
List of DeviceInfo objects.
|
|
128
|
+
"""
|
|
129
|
+
try:
|
|
130
|
+
result = subprocess.run(
|
|
131
|
+
[self.adb_path, "devices", "-l"],
|
|
132
|
+
capture_output=True,
|
|
133
|
+
text=True,
|
|
134
|
+
timeout=5,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
devices = []
|
|
138
|
+
for line in result.stdout.strip().split("\n")[1:]: # Skip header
|
|
139
|
+
if not line.strip():
|
|
140
|
+
continue
|
|
141
|
+
|
|
142
|
+
parts = line.split()
|
|
143
|
+
if len(parts) >= 2:
|
|
144
|
+
device_id = parts[0]
|
|
145
|
+
status = parts[1]
|
|
146
|
+
|
|
147
|
+
# Determine connection type
|
|
148
|
+
if ":" in device_id:
|
|
149
|
+
conn_type = ConnectionType.REMOTE
|
|
150
|
+
elif "emulator" in device_id:
|
|
151
|
+
conn_type = ConnectionType.USB # Emulator via USB
|
|
152
|
+
else:
|
|
153
|
+
conn_type = ConnectionType.USB
|
|
154
|
+
|
|
155
|
+
# Parse additional info
|
|
156
|
+
model = None
|
|
157
|
+
for part in parts[2:]:
|
|
158
|
+
if part.startswith("model:"):
|
|
159
|
+
model = part.split(":", 1)[1]
|
|
160
|
+
break
|
|
161
|
+
|
|
162
|
+
devices.append(
|
|
163
|
+
DeviceInfo(
|
|
164
|
+
device_id=device_id,
|
|
165
|
+
status=status,
|
|
166
|
+
connection_type=conn_type,
|
|
167
|
+
model=model,
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
return devices
|
|
172
|
+
|
|
173
|
+
except Exception as e:
|
|
174
|
+
print(f"Error listing devices: {e}")
|
|
175
|
+
return []
|
|
176
|
+
|
|
177
|
+
def get_device_info(self, device_id: str | None = None) -> DeviceInfo | None:
|
|
178
|
+
"""
|
|
179
|
+
Get detailed information about a device.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
device_id: Device ID. If None, uses first available device.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
DeviceInfo or None if not found.
|
|
186
|
+
"""
|
|
187
|
+
devices = self.list_devices()
|
|
188
|
+
|
|
189
|
+
if not devices:
|
|
190
|
+
return None
|
|
191
|
+
|
|
192
|
+
if device_id is None:
|
|
193
|
+
return devices[0]
|
|
194
|
+
|
|
195
|
+
for device in devices:
|
|
196
|
+
if device.device_id == device_id:
|
|
197
|
+
return device
|
|
198
|
+
|
|
199
|
+
return None
|
|
200
|
+
|
|
201
|
+
def is_connected(self, device_id: str | None = None) -> bool:
|
|
202
|
+
"""
|
|
203
|
+
Check if a device is connected.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
device_id: Device ID to check. If None, checks if any device is connected.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
True if connected, False otherwise.
|
|
210
|
+
"""
|
|
211
|
+
devices = self.list_devices()
|
|
212
|
+
|
|
213
|
+
if not devices:
|
|
214
|
+
return False
|
|
215
|
+
|
|
216
|
+
if device_id is None:
|
|
217
|
+
return any(d.status == "device" for d in devices)
|
|
218
|
+
|
|
219
|
+
return any(d.device_id == device_id and d.status == "device" for d in devices)
|
|
220
|
+
|
|
221
|
+
def enable_tcpip(
|
|
222
|
+
self, port: int = 5555, device_id: str | None = None
|
|
223
|
+
) -> tuple[bool, str]:
|
|
224
|
+
"""
|
|
225
|
+
Enable TCP/IP debugging on a USB-connected device.
|
|
226
|
+
|
|
227
|
+
This allows subsequent wireless connections to the device.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
port: TCP port for ADB (default: 5555).
|
|
231
|
+
device_id: Device ID. If None, uses first available device.
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
Tuple of (success, message).
|
|
235
|
+
|
|
236
|
+
Note:
|
|
237
|
+
The device must be connected via USB first.
|
|
238
|
+
After this, you can disconnect USB and connect via WiFi.
|
|
239
|
+
"""
|
|
240
|
+
try:
|
|
241
|
+
cmd = [self.adb_path]
|
|
242
|
+
if device_id:
|
|
243
|
+
cmd.extend(["-s", device_id])
|
|
244
|
+
cmd.extend(["tcpip", str(port)])
|
|
245
|
+
|
|
246
|
+
result = subprocess.run(
|
|
247
|
+
cmd, capture_output=True, text=True, encoding="utf-8", timeout=10
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
output = result.stdout + result.stderr
|
|
251
|
+
|
|
252
|
+
if "restarting" in output.lower() or result.returncode == 0:
|
|
253
|
+
time.sleep(TIMING_CONFIG.connection.adb_restart_delay)
|
|
254
|
+
return True, f"TCP/IP mode enabled on port {port}"
|
|
255
|
+
else:
|
|
256
|
+
return False, output.strip()
|
|
257
|
+
|
|
258
|
+
except Exception as e:
|
|
259
|
+
return False, f"Error enabling TCP/IP: {e}"
|
|
260
|
+
|
|
261
|
+
def get_device_ip(self, device_id: str | None = None) -> str | None:
|
|
262
|
+
"""
|
|
263
|
+
Get the IP address of a connected device.
|
|
264
|
+
|
|
265
|
+
Delegates to adb_plus.ip.get_wifi_ip() for better WiFi interface detection.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
device_id: Device ID. If None, uses first available device.
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
IP address string or None if not found.
|
|
272
|
+
"""
|
|
273
|
+
return get_wifi_ip(adb_path=self.adb_path, device_id=device_id)
|
|
274
|
+
|
|
275
|
+
def restart_server(self) -> tuple[bool, str]:
|
|
276
|
+
"""
|
|
277
|
+
Restart the ADB server.
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
Tuple of (success, message).
|
|
281
|
+
"""
|
|
282
|
+
try:
|
|
283
|
+
# Kill server
|
|
284
|
+
subprocess.run(
|
|
285
|
+
[self.adb_path, "kill-server"], capture_output=True, timeout=5
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
time.sleep(TIMING_CONFIG.connection.server_restart_delay)
|
|
289
|
+
|
|
290
|
+
# Start server
|
|
291
|
+
subprocess.run(
|
|
292
|
+
[self.adb_path, "start-server"], capture_output=True, timeout=5
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
return True, "ADB server restarted"
|
|
296
|
+
|
|
297
|
+
except Exception as e:
|
|
298
|
+
return False, f"Error restarting server: {e}"
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def quick_connect(address: str) -> tuple[bool, str]:
|
|
302
|
+
"""
|
|
303
|
+
Quick helper to connect to a remote device.
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
address: Device address (e.g., "192.168.1.100" or "192.168.1.100:5555").
|
|
307
|
+
|
|
308
|
+
Returns:
|
|
309
|
+
Tuple of (success, message).
|
|
310
|
+
"""
|
|
311
|
+
conn = ADBConnection()
|
|
312
|
+
return conn.connect(address)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def list_devices() -> list[DeviceInfo]:
|
|
316
|
+
"""
|
|
317
|
+
Quick helper to list connected devices.
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
List of DeviceInfo objects.
|
|
321
|
+
"""
|
|
322
|
+
conn = ADBConnection()
|
|
323
|
+
return conn.list_devices()
|