autoglm-gui 1.4.1__py3-none-any.whl → 1.5.1__py3-none-any.whl
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/__init__.py +11 -0
- AutoGLM_GUI/__main__.py +26 -4
- AutoGLM_GUI/actions/__init__.py +6 -0
- phone_agent/actions/handler_ios.py → AutoGLM_GUI/actions/handler.py +30 -112
- AutoGLM_GUI/actions/types.py +15 -0
- {phone_agent → AutoGLM_GUI}/adb/__init__.py +25 -23
- {phone_agent → AutoGLM_GUI}/adb/connection.py +5 -40
- {phone_agent → AutoGLM_GUI}/adb/device.py +12 -94
- {phone_agent → AutoGLM_GUI}/adb/input.py +6 -47
- AutoGLM_GUI/adb/screenshot.py +11 -0
- {phone_agent/config → AutoGLM_GUI/adb}/timing.py +1 -1
- AutoGLM_GUI/adb_plus/keyboard_installer.py +4 -2
- AutoGLM_GUI/adb_plus/screenshot.py +22 -1
- AutoGLM_GUI/adb_plus/serial.py +38 -20
- AutoGLM_GUI/adb_plus/touch.py +4 -9
- AutoGLM_GUI/agents/__init__.py +43 -12
- AutoGLM_GUI/agents/events.py +19 -0
- AutoGLM_GUI/agents/factory.py +31 -38
- AutoGLM_GUI/agents/glm/__init__.py +7 -0
- AutoGLM_GUI/agents/glm/agent.py +297 -0
- AutoGLM_GUI/agents/glm/message_builder.py +81 -0
- AutoGLM_GUI/agents/glm/parser.py +110 -0
- {phone_agent/config → AutoGLM_GUI/agents/glm}/prompts_en.py +7 -9
- {phone_agent/config → AutoGLM_GUI/agents/glm}/prompts_zh.py +18 -25
- AutoGLM_GUI/agents/mai/__init__.py +28 -0
- AutoGLM_GUI/agents/mai/agent.py +408 -0
- AutoGLM_GUI/agents/mai/parser.py +254 -0
- AutoGLM_GUI/agents/mai/prompts.py +103 -0
- AutoGLM_GUI/agents/mai/traj_memory.py +91 -0
- AutoGLM_GUI/agents/protocols.py +12 -8
- AutoGLM_GUI/agents/stream_runner.py +193 -0
- AutoGLM_GUI/api/__init__.py +40 -21
- AutoGLM_GUI/api/agents.py +181 -239
- AutoGLM_GUI/api/control.py +9 -6
- AutoGLM_GUI/api/devices.py +102 -12
- AutoGLM_GUI/api/history.py +104 -0
- AutoGLM_GUI/api/layered_agent.py +67 -15
- AutoGLM_GUI/api/media.py +64 -1
- AutoGLM_GUI/api/scheduled_tasks.py +98 -0
- AutoGLM_GUI/config.py +81 -0
- AutoGLM_GUI/config_manager.py +68 -51
- AutoGLM_GUI/device_manager.py +248 -29
- AutoGLM_GUI/device_protocol.py +1 -1
- AutoGLM_GUI/devices/adb_device.py +5 -10
- AutoGLM_GUI/devices/mock_device.py +4 -2
- AutoGLM_GUI/devices/remote_device.py +8 -3
- AutoGLM_GUI/history_manager.py +164 -0
- AutoGLM_GUI/model/__init__.py +5 -0
- AutoGLM_GUI/model/message_builder.py +69 -0
- AutoGLM_GUI/model/types.py +24 -0
- AutoGLM_GUI/models/__init__.py +10 -0
- AutoGLM_GUI/models/history.py +140 -0
- AutoGLM_GUI/models/scheduled_task.py +71 -0
- AutoGLM_GUI/parsers/__init__.py +22 -0
- AutoGLM_GUI/parsers/base.py +50 -0
- AutoGLM_GUI/parsers/phone_parser.py +58 -0
- AutoGLM_GUI/phone_agent_manager.py +62 -396
- AutoGLM_GUI/platform_utils.py +26 -0
- AutoGLM_GUI/prompt_config.py +15 -0
- AutoGLM_GUI/prompts/__init__.py +32 -0
- AutoGLM_GUI/scheduler_manager.py +350 -0
- AutoGLM_GUI/schemas.py +246 -72
- AutoGLM_GUI/scrcpy_stream.py +142 -24
- AutoGLM_GUI/socketio_server.py +100 -27
- AutoGLM_GUI/static/assets/{about-_XNhzQZX.js → about-CfwX1Cmc.js} +1 -1
- AutoGLM_GUI/static/assets/alert-dialog-CtGlN2IJ.js +1 -0
- AutoGLM_GUI/static/assets/chat-BYa-foUI.js +129 -0
- AutoGLM_GUI/static/assets/circle-alert-t08bEMPO.js +1 -0
- AutoGLM_GUI/static/assets/dialog-FNwZJFwk.js +45 -0
- AutoGLM_GUI/static/assets/eye-D0UPWCWC.js +1 -0
- AutoGLM_GUI/static/assets/history-CRo95B7i.js +1 -0
- AutoGLM_GUI/static/assets/{index-Cy8TmmHV.js → index-BaLMSqd3.js} +1 -1
- AutoGLM_GUI/static/assets/index-CTHbFvKl.js +11 -0
- AutoGLM_GUI/static/assets/index-CV7jGxGm.css +1 -0
- AutoGLM_GUI/static/assets/label-DJFevVmr.js +1 -0
- AutoGLM_GUI/static/assets/logs-RW09DyYY.js +1 -0
- AutoGLM_GUI/static/assets/popover--JTJrE5v.js +1 -0
- AutoGLM_GUI/static/assets/scheduled-tasks-DTRKsQXF.js +1 -0
- AutoGLM_GUI/static/assets/square-pen-CPK_K680.js +1 -0
- AutoGLM_GUI/static/assets/textarea-PRmVnWq5.js +1 -0
- AutoGLM_GUI/static/assets/workflows-CdcsAoaT.js +1 -0
- AutoGLM_GUI/static/index.html +2 -2
- AutoGLM_GUI/types.py +17 -0
- {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/METADATA +179 -130
- autoglm_gui-1.5.1.dist-info/RECORD +118 -0
- AutoGLM_GUI/agents/mai_adapter.py +0 -627
- AutoGLM_GUI/api/dual_model.py +0 -317
- AutoGLM_GUI/device_adapter.py +0 -263
- AutoGLM_GUI/dual_model/__init__.py +0 -53
- AutoGLM_GUI/dual_model/decision_model.py +0 -664
- AutoGLM_GUI/dual_model/dual_agent.py +0 -917
- AutoGLM_GUI/dual_model/protocols.py +0 -354
- AutoGLM_GUI/dual_model/vision_model.py +0 -442
- AutoGLM_GUI/mai_ui_adapter/agent_wrapper.py +0 -291
- AutoGLM_GUI/phone_agent_patches.py +0 -147
- AutoGLM_GUI/static/assets/chat-DwJpiAWf.js +0 -126
- AutoGLM_GUI/static/assets/dialog-B3uW4T8V.js +0 -45
- AutoGLM_GUI/static/assets/index-Cpv2gSF1.css +0 -1
- AutoGLM_GUI/static/assets/index-UYYauTly.js +0 -12
- AutoGLM_GUI/static/assets/workflows-Du_de-dt.js +0 -1
- autoglm_gui-1.4.1.dist-info/RECORD +0 -117
- mai_agent/base.py +0 -137
- mai_agent/mai_grounding_agent.py +0 -263
- mai_agent/mai_naivigation_agent.py +0 -526
- mai_agent/prompt.py +0 -148
- mai_agent/unified_memory.py +0 -67
- mai_agent/utils.py +0 -73
- phone_agent/__init__.py +0 -12
- phone_agent/actions/__init__.py +0 -5
- phone_agent/actions/handler.py +0 -400
- phone_agent/adb/screenshot.py +0 -108
- phone_agent/agent.py +0 -253
- phone_agent/agent_ios.py +0 -277
- phone_agent/config/__init__.py +0 -53
- phone_agent/config/apps_harmonyos.py +0 -256
- phone_agent/config/apps_ios.py +0 -339
- phone_agent/config/prompts.py +0 -80
- phone_agent/device_factory.py +0 -166
- phone_agent/hdc/__init__.py +0 -53
- phone_agent/hdc/connection.py +0 -384
- phone_agent/hdc/device.py +0 -269
- phone_agent/hdc/input.py +0 -145
- phone_agent/hdc/screenshot.py +0 -127
- phone_agent/model/__init__.py +0 -5
- phone_agent/model/client.py +0 -290
- phone_agent/xctest/__init__.py +0 -47
- phone_agent/xctest/connection.py +0 -379
- phone_agent/xctest/device.py +0 -472
- phone_agent/xctest/input.py +0 -311
- phone_agent/xctest/screenshot.py +0 -226
- {phone_agent/config → AutoGLM_GUI/adb}/apps.py +0 -0
- {phone_agent/config → AutoGLM_GUI}/i18n.py +0 -0
- {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/WHEEL +0 -0
- {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/entry_points.txt +0 -0
- {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,21 +3,13 @@
|
|
|
3
3
|
import subprocess
|
|
4
4
|
import time
|
|
5
5
|
|
|
6
|
-
from
|
|
7
|
-
from
|
|
6
|
+
from AutoGLM_GUI.adb.apps import APP_PACKAGES
|
|
7
|
+
from AutoGLM_GUI.adb.timing import TIMING_CONFIG
|
|
8
|
+
from AutoGLM_GUI.platform_utils import build_adb_command
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def get_current_app(device_id: str | None = None) -> str:
|
|
11
|
-
|
|
12
|
-
Get the currently focused app name.
|
|
13
|
-
|
|
14
|
-
Args:
|
|
15
|
-
device_id: Optional ADB device ID for multi-device setups.
|
|
16
|
-
|
|
17
|
-
Returns:
|
|
18
|
-
The app name if recognized, otherwise "System Home".
|
|
19
|
-
"""
|
|
20
|
-
adb_prefix = _get_adb_prefix(device_id)
|
|
12
|
+
adb_prefix = build_adb_command(device_id)
|
|
21
13
|
|
|
22
14
|
result = subprocess.run(
|
|
23
15
|
adb_prefix + ["shell", "dumpsys", "window"],
|
|
@@ -29,7 +21,6 @@ def get_current_app(device_id: str | None = None) -> str:
|
|
|
29
21
|
if not output:
|
|
30
22
|
raise ValueError("No output from dumpsys window")
|
|
31
23
|
|
|
32
|
-
# Parse window focus info
|
|
33
24
|
for line in output.split("\n"):
|
|
34
25
|
if "mCurrentFocus" in line or "mFocusedApp" in line:
|
|
35
26
|
for app_name, package in APP_PACKAGES.items():
|
|
@@ -42,19 +33,10 @@ def get_current_app(device_id: str | None = None) -> str:
|
|
|
42
33
|
def tap(
|
|
43
34
|
x: int, y: int, device_id: str | None = None, delay: float | None = None
|
|
44
35
|
) -> None:
|
|
45
|
-
"""
|
|
46
|
-
Tap at the specified coordinates.
|
|
47
|
-
|
|
48
|
-
Args:
|
|
49
|
-
x: X coordinate.
|
|
50
|
-
y: Y coordinate.
|
|
51
|
-
device_id: Optional ADB device ID.
|
|
52
|
-
delay: Delay in seconds after tap. If None, uses configured default.
|
|
53
|
-
"""
|
|
54
36
|
if delay is None:
|
|
55
37
|
delay = TIMING_CONFIG.device.default_tap_delay
|
|
56
38
|
|
|
57
|
-
adb_prefix =
|
|
39
|
+
adb_prefix = build_adb_command(device_id)
|
|
58
40
|
|
|
59
41
|
subprocess.run(
|
|
60
42
|
adb_prefix + ["shell", "input", "tap", str(x), str(y)], capture_output=True
|
|
@@ -65,19 +47,10 @@ def tap(
|
|
|
65
47
|
def double_tap(
|
|
66
48
|
x: int, y: int, device_id: str | None = None, delay: float | None = None
|
|
67
49
|
) -> None:
|
|
68
|
-
"""
|
|
69
|
-
Double tap at the specified coordinates.
|
|
70
|
-
|
|
71
|
-
Args:
|
|
72
|
-
x: X coordinate.
|
|
73
|
-
y: Y coordinate.
|
|
74
|
-
device_id: Optional ADB device ID.
|
|
75
|
-
delay: Delay in seconds after double tap. If None, uses configured default.
|
|
76
|
-
"""
|
|
77
50
|
if delay is None:
|
|
78
51
|
delay = TIMING_CONFIG.device.default_double_tap_delay
|
|
79
52
|
|
|
80
|
-
adb_prefix =
|
|
53
|
+
adb_prefix = build_adb_command(device_id)
|
|
81
54
|
|
|
82
55
|
subprocess.run(
|
|
83
56
|
adb_prefix + ["shell", "input", "tap", str(x), str(y)], capture_output=True
|
|
@@ -96,20 +69,10 @@ def long_press(
|
|
|
96
69
|
device_id: str | None = None,
|
|
97
70
|
delay: float | None = None,
|
|
98
71
|
) -> None:
|
|
99
|
-
"""
|
|
100
|
-
Long press at the specified coordinates.
|
|
101
|
-
|
|
102
|
-
Args:
|
|
103
|
-
x: X coordinate.
|
|
104
|
-
y: Y coordinate.
|
|
105
|
-
duration_ms: Duration of press in milliseconds.
|
|
106
|
-
device_id: Optional ADB device ID.
|
|
107
|
-
delay: Delay in seconds after long press. If None, uses configured default.
|
|
108
|
-
"""
|
|
109
72
|
if delay is None:
|
|
110
73
|
delay = TIMING_CONFIG.device.default_long_press_delay
|
|
111
74
|
|
|
112
|
-
adb_prefix =
|
|
75
|
+
adb_prefix = build_adb_command(device_id)
|
|
113
76
|
|
|
114
77
|
subprocess.run(
|
|
115
78
|
adb_prefix
|
|
@@ -128,28 +91,15 @@ def swipe(
|
|
|
128
91
|
device_id: str | None = None,
|
|
129
92
|
delay: float | None = None,
|
|
130
93
|
) -> None:
|
|
131
|
-
"""
|
|
132
|
-
Swipe from start to end coordinates.
|
|
133
|
-
|
|
134
|
-
Args:
|
|
135
|
-
start_x: Starting X coordinate.
|
|
136
|
-
start_y: Starting Y coordinate.
|
|
137
|
-
end_x: Ending X coordinate.
|
|
138
|
-
end_y: Ending Y coordinate.
|
|
139
|
-
duration_ms: Duration of swipe in milliseconds (auto-calculated if None).
|
|
140
|
-
device_id: Optional ADB device ID.
|
|
141
|
-
delay: Delay in seconds after swipe. If None, uses configured default.
|
|
142
|
-
"""
|
|
143
94
|
if delay is None:
|
|
144
95
|
delay = TIMING_CONFIG.device.default_swipe_delay
|
|
145
96
|
|
|
146
|
-
adb_prefix =
|
|
97
|
+
adb_prefix = build_adb_command(device_id)
|
|
147
98
|
|
|
148
99
|
if duration_ms is None:
|
|
149
|
-
# Calculate duration based on distance
|
|
150
100
|
dist_sq = (start_x - end_x) ** 2 + (start_y - end_y) ** 2
|
|
151
101
|
duration_ms = int(dist_sq / 1000)
|
|
152
|
-
duration_ms = max(1000, min(duration_ms, 2000))
|
|
102
|
+
duration_ms = max(1000, min(duration_ms, 2000))
|
|
153
103
|
|
|
154
104
|
subprocess.run(
|
|
155
105
|
adb_prefix
|
|
@@ -169,17 +119,10 @@ def swipe(
|
|
|
169
119
|
|
|
170
120
|
|
|
171
121
|
def back(device_id: str | None = None, delay: float | None = None) -> None:
|
|
172
|
-
"""
|
|
173
|
-
Press the back button.
|
|
174
|
-
|
|
175
|
-
Args:
|
|
176
|
-
device_id: Optional ADB device ID.
|
|
177
|
-
delay: Delay in seconds after pressing back. If None, uses configured default.
|
|
178
|
-
"""
|
|
179
122
|
if delay is None:
|
|
180
123
|
delay = TIMING_CONFIG.device.default_back_delay
|
|
181
124
|
|
|
182
|
-
adb_prefix =
|
|
125
|
+
adb_prefix = build_adb_command(device_id)
|
|
183
126
|
|
|
184
127
|
subprocess.run(
|
|
185
128
|
adb_prefix + ["shell", "input", "keyevent", "4"], capture_output=True
|
|
@@ -188,17 +131,10 @@ def back(device_id: str | None = None, delay: float | None = None) -> None:
|
|
|
188
131
|
|
|
189
132
|
|
|
190
133
|
def home(device_id: str | None = None, delay: float | None = None) -> None:
|
|
191
|
-
"""
|
|
192
|
-
Press the home button.
|
|
193
|
-
|
|
194
|
-
Args:
|
|
195
|
-
device_id: Optional ADB device ID.
|
|
196
|
-
delay: Delay in seconds after pressing home. If None, uses configured default.
|
|
197
|
-
"""
|
|
198
134
|
if delay is None:
|
|
199
135
|
delay = TIMING_CONFIG.device.default_home_delay
|
|
200
136
|
|
|
201
|
-
adb_prefix =
|
|
137
|
+
adb_prefix = build_adb_command(device_id)
|
|
202
138
|
|
|
203
139
|
subprocess.run(
|
|
204
140
|
adb_prefix + ["shell", "input", "keyevent", "KEYCODE_HOME"], capture_output=True
|
|
@@ -209,24 +145,13 @@ def home(device_id: str | None = None, delay: float | None = None) -> None:
|
|
|
209
145
|
def launch_app(
|
|
210
146
|
app_name: str, device_id: str | None = None, delay: float | None = None
|
|
211
147
|
) -> bool:
|
|
212
|
-
"""
|
|
213
|
-
Launch an app by name.
|
|
214
|
-
|
|
215
|
-
Args:
|
|
216
|
-
app_name: The app name (must be in APP_PACKAGES).
|
|
217
|
-
device_id: Optional ADB device ID.
|
|
218
|
-
delay: Delay in seconds after launching. If None, uses configured default.
|
|
219
|
-
|
|
220
|
-
Returns:
|
|
221
|
-
True if app was launched, False if app not found.
|
|
222
|
-
"""
|
|
223
148
|
if delay is None:
|
|
224
149
|
delay = TIMING_CONFIG.device.default_launch_delay
|
|
225
150
|
|
|
226
151
|
if app_name not in APP_PACKAGES:
|
|
227
152
|
return False
|
|
228
153
|
|
|
229
|
-
adb_prefix =
|
|
154
|
+
adb_prefix = build_adb_command(device_id)
|
|
230
155
|
package = APP_PACKAGES[app_name]
|
|
231
156
|
|
|
232
157
|
subprocess.run(
|
|
@@ -244,10 +169,3 @@ def launch_app(
|
|
|
244
169
|
)
|
|
245
170
|
time.sleep(delay)
|
|
246
171
|
return True
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
def _get_adb_prefix(device_id: str | None) -> list:
|
|
250
|
-
"""Get ADB command prefix with optional device specifier."""
|
|
251
|
-
if device_id:
|
|
252
|
-
return ["adb", "-s", device_id]
|
|
253
|
-
return ["adb"]
|
|
@@ -3,20 +3,11 @@
|
|
|
3
3
|
import base64
|
|
4
4
|
import subprocess
|
|
5
5
|
|
|
6
|
+
from AutoGLM_GUI.platform_utils import build_adb_command
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
def type_text(text: str, device_id: str | None = None) -> None:
|
|
8
|
-
|
|
9
|
-
Type text into the currently focused input field using ADB Keyboard.
|
|
10
|
-
|
|
11
|
-
Args:
|
|
12
|
-
text: The text to type.
|
|
13
|
-
device_id: Optional ADB device ID for multi-device setups.
|
|
14
|
-
|
|
15
|
-
Note:
|
|
16
|
-
Requires ADB Keyboard to be installed on the device.
|
|
17
|
-
See: https://github.com/nicnocquee/AdbKeyboard
|
|
18
|
-
"""
|
|
19
|
-
adb_prefix = _get_adb_prefix(device_id)
|
|
10
|
+
adb_prefix = build_adb_command(device_id)
|
|
20
11
|
encoded_text = base64.b64encode(text.encode("utf-8")).decode("utf-8")
|
|
21
12
|
|
|
22
13
|
subprocess.run(
|
|
@@ -37,13 +28,7 @@ def type_text(text: str, device_id: str | None = None) -> None:
|
|
|
37
28
|
|
|
38
29
|
|
|
39
30
|
def clear_text(device_id: str | None = None) -> None:
|
|
40
|
-
|
|
41
|
-
Clear text in the currently focused input field.
|
|
42
|
-
|
|
43
|
-
Args:
|
|
44
|
-
device_id: Optional ADB device ID for multi-device setups.
|
|
45
|
-
"""
|
|
46
|
-
adb_prefix = _get_adb_prefix(device_id)
|
|
31
|
+
adb_prefix = build_adb_command(device_id)
|
|
47
32
|
|
|
48
33
|
subprocess.run(
|
|
49
34
|
adb_prefix + ["shell", "am", "broadcast", "-a", "ADB_CLEAR_TEXT"],
|
|
@@ -53,18 +38,8 @@ def clear_text(device_id: str | None = None) -> None:
|
|
|
53
38
|
|
|
54
39
|
|
|
55
40
|
def detect_and_set_adb_keyboard(device_id: str | None = None) -> str:
|
|
56
|
-
|
|
57
|
-
Detect current keyboard and switch to ADB Keyboard if needed.
|
|
58
|
-
|
|
59
|
-
Args:
|
|
60
|
-
device_id: Optional ADB device ID for multi-device setups.
|
|
61
|
-
|
|
62
|
-
Returns:
|
|
63
|
-
The original keyboard IME identifier for later restoration.
|
|
64
|
-
"""
|
|
65
|
-
adb_prefix = _get_adb_prefix(device_id)
|
|
41
|
+
adb_prefix = build_adb_command(device_id)
|
|
66
42
|
|
|
67
|
-
# Get current IME
|
|
68
43
|
result = subprocess.run(
|
|
69
44
|
adb_prefix + ["shell", "settings", "get", "secure", "default_input_method"],
|
|
70
45
|
capture_output=True,
|
|
@@ -72,7 +47,6 @@ def detect_and_set_adb_keyboard(device_id: str | None = None) -> str:
|
|
|
72
47
|
)
|
|
73
48
|
current_ime = (result.stdout + result.stderr).strip()
|
|
74
49
|
|
|
75
|
-
# Switch to ADB Keyboard if not already set
|
|
76
50
|
if "com.android.adbkeyboard/.AdbIME" not in current_ime:
|
|
77
51
|
subprocess.run(
|
|
78
52
|
adb_prefix + ["shell", "ime", "set", "com.android.adbkeyboard/.AdbIME"],
|
|
@@ -80,29 +54,14 @@ def detect_and_set_adb_keyboard(device_id: str | None = None) -> str:
|
|
|
80
54
|
text=True,
|
|
81
55
|
)
|
|
82
56
|
|
|
83
|
-
# Warm up the keyboard
|
|
84
57
|
type_text("", device_id)
|
|
85
58
|
|
|
86
59
|
return current_ime
|
|
87
60
|
|
|
88
61
|
|
|
89
62
|
def restore_keyboard(ime: str, device_id: str | None = None) -> None:
|
|
90
|
-
|
|
91
|
-
Restore the original keyboard IME.
|
|
92
|
-
|
|
93
|
-
Args:
|
|
94
|
-
ime: The IME identifier to restore.
|
|
95
|
-
device_id: Optional ADB device ID for multi-device setups.
|
|
96
|
-
"""
|
|
97
|
-
adb_prefix = _get_adb_prefix(device_id)
|
|
63
|
+
adb_prefix = build_adb_command(device_id)
|
|
98
64
|
|
|
99
65
|
subprocess.run(
|
|
100
66
|
adb_prefix + ["shell", "ime", "set", ime], capture_output=True, text=True
|
|
101
67
|
)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def _get_adb_prefix(device_id: str | None) -> list:
|
|
105
|
-
"""Get ADB command prefix with optional device specifier."""
|
|
106
|
-
if device_id:
|
|
107
|
-
return ["adb", "-s", device_id]
|
|
108
|
-
return ["adb"]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Screenshot utilities for capturing Android device screen.
|
|
2
|
+
|
|
3
|
+
DEPRECATED: This module now delegates to adb_plus.screenshot for the actual implementation.
|
|
4
|
+
Use adb_plus.screenshot directly for new code.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from AutoGLM_GUI.adb_plus.screenshot import Screenshot, capture_screenshot
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_screenshot(device_id: str | None = None, timeout: int = 10) -> Screenshot:
|
|
11
|
+
return capture_screenshot(device_id=device_id, timeout=timeout)
|
|
@@ -140,7 +140,7 @@ def update_timing_config(
|
|
|
140
140
|
connection: New connection timing configuration.
|
|
141
141
|
|
|
142
142
|
Example:
|
|
143
|
-
>>> from
|
|
143
|
+
>>> from AutoGLM_GUI.adb.timing import update_timing_config, ActionTimingConfig
|
|
144
144
|
>>> custom_action = ActionTimingConfig(
|
|
145
145
|
... keyboard_switch_delay=0.5,
|
|
146
146
|
... text_input_delay=0.5
|
|
@@ -104,8 +104,10 @@ class ADBKeyboardInstaller:
|
|
|
104
104
|
from importlib.resources import files
|
|
105
105
|
|
|
106
106
|
logger.debug("Searching for bundled APK in wheel package")
|
|
107
|
-
resource =
|
|
108
|
-
"
|
|
107
|
+
resource = (
|
|
108
|
+
files("AutoGLM_GUI")
|
|
109
|
+
.joinpath("resources/apks")
|
|
110
|
+
.joinpath(ADB_KEYBOARD_APK_FILENAME)
|
|
109
111
|
)
|
|
110
112
|
# Convert to Path
|
|
111
113
|
if hasattr(resource, "read_bytes"):
|
|
@@ -13,6 +13,8 @@ from io import BytesIO
|
|
|
13
13
|
|
|
14
14
|
from PIL import Image
|
|
15
15
|
|
|
16
|
+
from AutoGLM_GUI.exceptions import DeviceNotAvailableError
|
|
17
|
+
|
|
16
18
|
|
|
17
19
|
PNG_SIGNATURE = b"\x89PNG\r\n\x1a\n"
|
|
18
20
|
|
|
@@ -44,9 +46,13 @@ def capture_screenshot(
|
|
|
44
46
|
|
|
45
47
|
Returns:
|
|
46
48
|
Screenshot object; falls back to a black image on failure.
|
|
49
|
+
|
|
50
|
+
Raises:
|
|
51
|
+
DeviceNotAvailableError: When device is not found or offline.
|
|
47
52
|
"""
|
|
48
53
|
attempts = max(1, retries + 1)
|
|
49
54
|
for _ in range(attempts):
|
|
55
|
+
# _try_capture may raise DeviceNotAvailableError, let it propagate
|
|
50
56
|
data = _try_capture(device_id=device_id, adb_path=adb_path, timeout=timeout)
|
|
51
57
|
if not data:
|
|
52
58
|
continue
|
|
@@ -72,7 +78,11 @@ def capture_screenshot(
|
|
|
72
78
|
|
|
73
79
|
|
|
74
80
|
def _try_capture(device_id: str | None, adb_path: str, timeout: int) -> bytes | None:
|
|
75
|
-
"""Run exec-out screencap and return raw bytes or None on failure.
|
|
81
|
+
"""Run exec-out screencap and return raw bytes or None on failure.
|
|
82
|
+
|
|
83
|
+
Raises:
|
|
84
|
+
DeviceNotAvailableError: When device is not found or offline.
|
|
85
|
+
"""
|
|
76
86
|
cmd: list[str | bytes] = [adb_path]
|
|
77
87
|
if device_id:
|
|
78
88
|
cmd.extend(["-s", device_id])
|
|
@@ -85,9 +95,20 @@ def _try_capture(device_id: str | None, adb_path: str, timeout: int) -> bytes |
|
|
|
85
95
|
timeout=timeout,
|
|
86
96
|
)
|
|
87
97
|
if result.returncode != 0:
|
|
98
|
+
# Check for device not found or offline errors
|
|
99
|
+
stderr = (
|
|
100
|
+
result.stderr.decode("utf-8", errors="ignore") if result.stderr else ""
|
|
101
|
+
)
|
|
102
|
+
stderr_lower = stderr.lower()
|
|
103
|
+
if "device not found" in stderr_lower or "offline" in stderr_lower:
|
|
104
|
+
raise DeviceNotAvailableError(
|
|
105
|
+
f"Device {device_id} not found or offline"
|
|
106
|
+
)
|
|
88
107
|
return None
|
|
89
108
|
# stdout should hold the PNG data
|
|
90
109
|
return result.stdout if isinstance(result.stdout, (bytes, bytearray)) else None
|
|
110
|
+
except DeviceNotAvailableError:
|
|
111
|
+
raise # Re-raise to caller
|
|
91
112
|
except Exception:
|
|
92
113
|
return None
|
|
93
114
|
|
AutoGLM_GUI/adb_plus/serial.py
CHANGED
|
@@ -49,12 +49,22 @@ def extract_serial_from_mdns(device_id: str) -> Optional[str]:
|
|
|
49
49
|
return None
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
# Serial number properties to try, in order of preference
|
|
53
|
+
_SERIAL_PROPS = [
|
|
54
|
+
"ro.serialno",
|
|
55
|
+
"ro.boot.serialno",
|
|
56
|
+
"ro.product.serial",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_device_serial(device_id: str, adb_path: str = "adb") -> str:
|
|
53
61
|
"""
|
|
54
62
|
Get the real hardware serial number of a device.
|
|
55
63
|
|
|
56
64
|
For mDNS devices, attempts to extract serial from service name first.
|
|
57
65
|
Falls back to getprop for USB/WiFi devices or if extraction fails.
|
|
66
|
+
If all methods fail, returns device_id as fallback (for emulators or
|
|
67
|
+
restricted devices that don't expose serial number).
|
|
58
68
|
|
|
59
69
|
This works for both USB and WiFi connected devices,
|
|
60
70
|
returning the actual hardware serial number (ro.serialno).
|
|
@@ -64,7 +74,8 @@ def get_device_serial(device_id: str, adb_path: str = "adb") -> str | None:
|
|
|
64
74
|
adb_path: Path to adb executable (default: "adb")
|
|
65
75
|
|
|
66
76
|
Returns:
|
|
67
|
-
The device hardware serial number
|
|
77
|
+
The device hardware serial number. Always returns a value - uses
|
|
78
|
+
device_id as fallback if serial cannot be obtained.
|
|
68
79
|
"""
|
|
69
80
|
from AutoGLM_GUI.logger import logger
|
|
70
81
|
|
|
@@ -74,21 +85,28 @@ def get_device_serial(device_id: str, adb_path: str = "adb") -> str | None:
|
|
|
74
85
|
logger.debug(f"Extracted serial from mDNS name: {device_id} → {mdns_serial}")
|
|
75
86
|
return mdns_serial
|
|
76
87
|
|
|
77
|
-
#
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
88
|
+
# Try multiple serial properties (some emulators use different props)
|
|
89
|
+
for prop in _SERIAL_PROPS:
|
|
90
|
+
try:
|
|
91
|
+
result = run_cmd_silently_sync(
|
|
92
|
+
[adb_path, "-s", device_id, "shell", "getprop", prop],
|
|
93
|
+
timeout=5, # Increased timeout for network devices
|
|
94
|
+
)
|
|
95
|
+
if result.returncode == 0:
|
|
96
|
+
serial = result.stdout.strip()
|
|
97
|
+
# Filter out error messages and empty values
|
|
98
|
+
if serial and not serial.startswith("error:") and serial != "unknown":
|
|
99
|
+
logger.debug(f"Got serial via {prop}: {device_id} → {serial}")
|
|
100
|
+
return serial
|
|
101
|
+
except Exception as e:
|
|
102
|
+
logger.debug(f"Failed to get serial via {prop} for {device_id}: {e}")
|
|
103
|
+
continue
|
|
104
|
+
|
|
105
|
+
# Fallback: Use device_id itself as serial
|
|
106
|
+
# This handles emulators (MuMu, Nox, etc.) and restricted devices
|
|
107
|
+
# that don't expose serial number via getprop
|
|
108
|
+
logger.warning(
|
|
109
|
+
f"Could not get hardware serial for {device_id}, "
|
|
110
|
+
f"using device_id as serial (emulator/restricted device)"
|
|
111
|
+
)
|
|
112
|
+
return device_id
|
AutoGLM_GUI/adb_plus/touch.py
CHANGED
|
@@ -3,12 +3,7 @@
|
|
|
3
3
|
import subprocess
|
|
4
4
|
import time
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
def _get_adb_prefix(device_id: str | None, adb_path: str = "adb") -> list[str]:
|
|
8
|
-
"""Get ADB command prefix with optional device specifier."""
|
|
9
|
-
if device_id:
|
|
10
|
-
return [adb_path, "-s", device_id]
|
|
11
|
-
return [adb_path]
|
|
6
|
+
from AutoGLM_GUI.platform_utils import build_adb_command
|
|
12
7
|
|
|
13
8
|
|
|
14
9
|
def touch_down(
|
|
@@ -28,7 +23,7 @@ def touch_down(
|
|
|
28
23
|
delay: Delay in seconds after event (default: 0.0 for real-time).
|
|
29
24
|
adb_path: Path to adb binary.
|
|
30
25
|
"""
|
|
31
|
-
adb_prefix =
|
|
26
|
+
adb_prefix = build_adb_command(device_id, adb_path)
|
|
32
27
|
|
|
33
28
|
subprocess.run(
|
|
34
29
|
adb_prefix + ["shell", "input", "motionevent", "DOWN", str(x), str(y)],
|
|
@@ -55,7 +50,7 @@ def touch_move(
|
|
|
55
50
|
delay: Delay in seconds after event (default: 0.0 for real-time).
|
|
56
51
|
adb_path: Path to adb binary.
|
|
57
52
|
"""
|
|
58
|
-
adb_prefix =
|
|
53
|
+
adb_prefix = build_adb_command(device_id, adb_path)
|
|
59
54
|
|
|
60
55
|
subprocess.run(
|
|
61
56
|
adb_prefix + ["shell", "input", "motionevent", "MOVE", str(x), str(y)],
|
|
@@ -82,7 +77,7 @@ def touch_up(
|
|
|
82
77
|
delay: Delay in seconds after event (default: 0.0 for real-time).
|
|
83
78
|
adb_path: Path to adb binary.
|
|
84
79
|
"""
|
|
85
|
-
adb_prefix =
|
|
80
|
+
adb_prefix = build_adb_command(device_id, adb_path)
|
|
86
81
|
|
|
87
82
|
subprocess.run(
|
|
88
83
|
adb_prefix + ["shell", "input", "motionevent", "UP", str(x), str(y)],
|
AutoGLM_GUI/agents/__init__.py
CHANGED
|
@@ -1,20 +1,51 @@
|
|
|
1
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Callable
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def register_agent(agent_type: str, creator: Callable) -> None:
|
|
7
|
+
from .factory import register_agent as _register_agent
|
|
8
|
+
|
|
9
|
+
_register_agent(agent_type=agent_type, creator=creator)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_agent(
|
|
13
|
+
agent_type: str,
|
|
14
|
+
model_config,
|
|
15
|
+
agent_config,
|
|
16
|
+
agent_specific_config,
|
|
17
|
+
device,
|
|
18
|
+
takeover_callback: Callable | None = None,
|
|
19
|
+
confirmation_callback: Callable | None = None,
|
|
20
|
+
):
|
|
21
|
+
from .factory import create_agent as _create_agent
|
|
22
|
+
|
|
23
|
+
return _create_agent(
|
|
24
|
+
agent_type=agent_type,
|
|
25
|
+
model_config=model_config,
|
|
26
|
+
agent_config=agent_config,
|
|
27
|
+
agent_specific_config=agent_specific_config,
|
|
28
|
+
device=device,
|
|
29
|
+
takeover_callback=takeover_callback,
|
|
30
|
+
confirmation_callback=confirmation_callback,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def list_agent_types() -> list[str]:
|
|
35
|
+
from .factory import list_agent_types as _list_agent_types
|
|
36
|
+
|
|
37
|
+
return _list_agent_types()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def is_agent_type_registered(agent_type: str) -> bool:
|
|
41
|
+
from .factory import is_agent_type_registered as _is_agent_type_registered
|
|
42
|
+
|
|
43
|
+
return _is_agent_type_registered(agent_type)
|
|
2
44
|
|
|
3
|
-
from .factory import (
|
|
4
|
-
create_agent,
|
|
5
|
-
is_agent_type_registered,
|
|
6
|
-
list_agent_types,
|
|
7
|
-
register_agent,
|
|
8
|
-
)
|
|
9
|
-
from .mai_adapter import MAIAgentAdapter, MAIAgentConfig
|
|
10
45
|
|
|
11
46
|
__all__ = [
|
|
12
|
-
# Factory
|
|
13
47
|
"create_agent",
|
|
14
48
|
"register_agent",
|
|
15
49
|
"list_agent_types",
|
|
16
50
|
"is_agent_type_registered",
|
|
17
|
-
# Adapters
|
|
18
|
-
"MAIAgentAdapter",
|
|
19
|
-
"MAIAgentConfig",
|
|
20
51
|
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Any, TypedDict
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class AgentEventType(str, Enum):
|
|
6
|
+
"""Agent 事件类型."""
|
|
7
|
+
|
|
8
|
+
THINKING = "thinking_chunk"
|
|
9
|
+
STEP = "step"
|
|
10
|
+
DONE = "done"
|
|
11
|
+
ERROR = "error"
|
|
12
|
+
ABORTED = "aborted"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AgentEvent(TypedDict):
|
|
16
|
+
"""Agent 事件(统一类型)."""
|
|
17
|
+
|
|
18
|
+
type: str # 使用字符串以兼容现有 SSE 类型
|
|
19
|
+
data: dict[str, Any]
|