autoglm-gui 1.0.0__py3-none-any.whl → 1.0.2__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.
Files changed (41) hide show
  1. AutoGLM_GUI/api/devices.py +49 -0
  2. AutoGLM_GUI/schemas.py +16 -0
  3. AutoGLM_GUI/static/assets/{about-29B5FDM8.js → about-BOnRPlKQ.js} +1 -1
  4. AutoGLM_GUI/static/assets/chat-CGW6uMKB.js +149 -0
  5. AutoGLM_GUI/static/assets/{index-mVNV0VwM.js → index-CRFVU0eu.js} +1 -1
  6. AutoGLM_GUI/static/assets/{index-wu8Wjf12.js → index-DH-Dl4tK.js} +5 -5
  7. AutoGLM_GUI/static/assets/index-DzUQ89YC.css +1 -0
  8. AutoGLM_GUI/static/index.html +2 -2
  9. {autoglm_gui-1.0.0.dist-info → autoglm_gui-1.0.2.dist-info}/METADATA +9 -4
  10. autoglm_gui-1.0.2.dist-info/RECORD +73 -0
  11. phone_agent/__init__.py +3 -2
  12. phone_agent/actions/handler.py +124 -31
  13. phone_agent/actions/handler_ios.py +278 -0
  14. phone_agent/adb/connection.py +14 -5
  15. phone_agent/adb/device.py +47 -16
  16. phone_agent/agent.py +8 -8
  17. phone_agent/agent_ios.py +277 -0
  18. phone_agent/config/__init__.py +18 -0
  19. phone_agent/config/apps.py +1 -1
  20. phone_agent/config/apps_harmonyos.py +256 -0
  21. phone_agent/config/apps_ios.py +339 -0
  22. phone_agent/config/i18n.py +8 -0
  23. phone_agent/config/timing.py +167 -0
  24. phone_agent/device_factory.py +166 -0
  25. phone_agent/hdc/__init__.py +53 -0
  26. phone_agent/hdc/connection.py +384 -0
  27. phone_agent/hdc/device.py +269 -0
  28. phone_agent/hdc/input.py +145 -0
  29. phone_agent/hdc/screenshot.py +127 -0
  30. phone_agent/model/client.py +104 -4
  31. phone_agent/xctest/__init__.py +47 -0
  32. phone_agent/xctest/connection.py +379 -0
  33. phone_agent/xctest/device.py +472 -0
  34. phone_agent/xctest/input.py +311 -0
  35. phone_agent/xctest/screenshot.py +226 -0
  36. AutoGLM_GUI/static/assets/chat-DTN2oKtA.js +0 -149
  37. AutoGLM_GUI/static/assets/index-Dy550Qqg.css +0 -1
  38. autoglm_gui-1.0.0.dist-info/RECORD +0 -57
  39. {autoglm_gui-1.0.0.dist-info → autoglm_gui-1.0.2.dist-info}/WHEEL +0 -0
  40. {autoglm_gui-1.0.0.dist-info → autoglm_gui-1.0.2.dist-info}/entry_points.txt +0 -0
  41. {autoglm_gui-1.0.0.dist-info → autoglm_gui-1.0.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,311 @@
1
+ """Input utilities for iOS device text input via WebDriverAgent."""
2
+
3
+ import time
4
+
5
+
6
+ def _get_wda_session_url(wda_url: str, session_id: str | None, endpoint: str) -> str:
7
+ """
8
+ Get the correct WDA URL for a session endpoint.
9
+
10
+ Args:
11
+ wda_url: Base WDA URL.
12
+ session_id: Optional session ID.
13
+ endpoint: The endpoint path.
14
+
15
+ Returns:
16
+ Full URL for the endpoint.
17
+ """
18
+ base = wda_url.rstrip("/")
19
+ if session_id:
20
+ return f"{base}/session/{session_id}/{endpoint}"
21
+ else:
22
+ # Try to use WDA endpoints without session when possible
23
+ return f"{base}/{endpoint}"
24
+
25
+
26
+ def type_text(
27
+ text: str,
28
+ wda_url: str = "http://localhost:8100",
29
+ session_id: str | None = None,
30
+ frequency: int = 60,
31
+ ) -> None:
32
+ """
33
+ Type text into the currently focused input field.
34
+
35
+ Args:
36
+ text: The text to type.
37
+ wda_url: WebDriverAgent URL.
38
+ session_id: Optional WDA session ID.
39
+ frequency: Typing frequency (keys per minute). Default is 60.
40
+
41
+ Note:
42
+ The input field must be focused before calling this function.
43
+ Use tap() to focus on the input field first.
44
+ """
45
+ try:
46
+ import requests
47
+
48
+ url = _get_wda_session_url(wda_url, session_id, "wda/keys")
49
+
50
+ # Send text to WDA
51
+ response = requests.post(
52
+ url,
53
+ json={"value": list(text), "frequency": frequency},
54
+ timeout=30,
55
+ verify=False,
56
+ )
57
+
58
+ if response.status_code not in (200, 201):
59
+ print(
60
+ f"Warning: Text input may have failed. Status: {response.status_code}"
61
+ )
62
+
63
+ except ImportError:
64
+ print("Error: requests library required. Install: pip install requests")
65
+ except Exception as e:
66
+ print(f"Error typing text: {e}")
67
+
68
+
69
+ def clear_text(
70
+ wda_url: str = "http://localhost:8100",
71
+ session_id: str | None = None,
72
+ ) -> None:
73
+ """
74
+ Clear text in the currently focused input field.
75
+
76
+ Args:
77
+ wda_url: WebDriverAgent URL.
78
+ session_id: Optional WDA session ID.
79
+
80
+ Note:
81
+ This sends a clear command to the active element.
82
+ The input field must be focused before calling this function.
83
+ """
84
+ try:
85
+ import requests
86
+
87
+ # First, try to get the active element
88
+ url = _get_wda_session_url(wda_url, session_id, "element/active")
89
+
90
+ response = requests.get(url, timeout=10, verify=False)
91
+
92
+ if response.status_code == 200:
93
+ data = response.json()
94
+ element_id = data.get("value", {}).get("ELEMENT") or data.get(
95
+ "value", {}
96
+ ).get("element-6066-11e4-a52e-4f735466cecf")
97
+
98
+ if element_id:
99
+ # Clear the element
100
+ clear_url = _get_wda_session_url(
101
+ wda_url, session_id, f"element/{element_id}/clear"
102
+ )
103
+ requests.post(clear_url, timeout=10, verify=False)
104
+ return
105
+
106
+ # Fallback: send backspace commands
107
+ _clear_with_backspace(wda_url, session_id)
108
+
109
+ except ImportError:
110
+ print("Error: requests library required. Install: pip install requests")
111
+ except Exception as e:
112
+ print(f"Error clearing text: {e}")
113
+
114
+
115
+ def _clear_with_backspace(
116
+ wda_url: str = "http://localhost:8100",
117
+ session_id: str | None = None,
118
+ max_backspaces: int = 100,
119
+ ) -> None:
120
+ """
121
+ Clear text by sending backspace keys.
122
+
123
+ Args:
124
+ wda_url: WebDriverAgent URL.
125
+ session_id: Optional WDA session ID.
126
+ max_backspaces: Maximum number of backspaces to send.
127
+ """
128
+ try:
129
+ import requests
130
+
131
+ url = _get_wda_session_url(wda_url, session_id, "wda/keys")
132
+
133
+ # Send backspace character multiple times
134
+ backspace_char = "\u0008" # Backspace Unicode character
135
+ requests.post(
136
+ url,
137
+ json={"value": [backspace_char] * max_backspaces},
138
+ timeout=10,
139
+ verify=False,
140
+ )
141
+
142
+ except Exception as e:
143
+ print(f"Error clearing with backspace: {e}")
144
+
145
+
146
+ def send_keys(
147
+ keys: list[str],
148
+ wda_url: str = "http://localhost:8100",
149
+ session_id: str | None = None,
150
+ ) -> None:
151
+ """
152
+ Send a sequence of keys.
153
+
154
+ Args:
155
+ keys: List of keys to send.
156
+ wda_url: WebDriverAgent URL.
157
+ session_id: Optional WDA session ID.
158
+
159
+ Example:
160
+ >>> send_keys(["H", "e", "l", "l", "o"])
161
+ >>> send_keys(["\n"]) # Send enter key
162
+ """
163
+ try:
164
+ import requests
165
+
166
+ url = _get_wda_session_url(wda_url, session_id, "wda/keys")
167
+
168
+ requests.post(url, json={"value": keys}, timeout=10, verify=False)
169
+
170
+ except ImportError:
171
+ print("Error: requests library required. Install: pip install requests")
172
+ except Exception as e:
173
+ print(f"Error sending keys: {e}")
174
+
175
+
176
+ def press_enter(
177
+ wda_url: str = "http://localhost:8100",
178
+ session_id: str | None = None,
179
+ delay: float = 0.5,
180
+ ) -> None:
181
+ """
182
+ Press the Enter/Return key.
183
+
184
+ Args:
185
+ wda_url: WebDriverAgent URL.
186
+ session_id: Optional WDA session ID.
187
+ delay: Delay in seconds after pressing enter.
188
+ """
189
+ send_keys(["\n"], wda_url, session_id)
190
+ time.sleep(delay)
191
+
192
+
193
+ def hide_keyboard(
194
+ wda_url: str = "http://localhost:8100",
195
+ session_id: str | None = None,
196
+ ) -> None:
197
+ """
198
+ Hide the on-screen keyboard.
199
+
200
+ Args:
201
+ wda_url: WebDriverAgent URL.
202
+ session_id: Optional WDA session ID.
203
+ """
204
+ try:
205
+ import requests
206
+
207
+ url = f"{wda_url.rstrip('/')}/wda/keyboard/dismiss"
208
+
209
+ requests.post(url, timeout=10, verify=False)
210
+
211
+ except ImportError:
212
+ print("Error: requests library required. Install: pip install requests")
213
+ except Exception as e:
214
+ print(f"Error hiding keyboard: {e}")
215
+
216
+
217
+ def is_keyboard_shown(
218
+ wda_url: str = "http://localhost:8100",
219
+ session_id: str | None = None,
220
+ ) -> bool:
221
+ """
222
+ Check if the on-screen keyboard is currently shown.
223
+
224
+ Args:
225
+ wda_url: WebDriverAgent URL.
226
+ session_id: Optional WDA session ID.
227
+
228
+ Returns:
229
+ True if keyboard is shown, False otherwise.
230
+ """
231
+ try:
232
+ import requests
233
+
234
+ url = _get_wda_session_url(wda_url, session_id, "wda/keyboard/shown")
235
+
236
+ response = requests.get(url, timeout=5, verify=False)
237
+
238
+ if response.status_code == 200:
239
+ data = response.json()
240
+ return data.get("value", False)
241
+
242
+ except ImportError:
243
+ print("Error: requests library required. Install: pip install requests")
244
+ except Exception:
245
+ pass
246
+
247
+ return False
248
+
249
+
250
+ def set_pasteboard(
251
+ text: str,
252
+ wda_url: str = "http://localhost:8100",
253
+ ) -> None:
254
+ """
255
+ Set the device pasteboard (clipboard) content.
256
+
257
+ Args:
258
+ text: Text to set in pasteboard.
259
+ wda_url: WebDriverAgent URL.
260
+
261
+ Note:
262
+ This can be useful for inputting large amounts of text.
263
+ After setting pasteboard, you can simulate paste gesture.
264
+ """
265
+ try:
266
+ import requests
267
+
268
+ url = f"{wda_url.rstrip('/')}/wda/setPasteboard"
269
+
270
+ requests.post(
271
+ url,
272
+ json={"content": text, "contentType": "plaintext"},
273
+ timeout=10,
274
+ verify=False,
275
+ )
276
+
277
+ except ImportError:
278
+ print("Error: requests library required. Install: pip install requests")
279
+ except Exception as e:
280
+ print(f"Error setting pasteboard: {e}")
281
+
282
+
283
+ def get_pasteboard(
284
+ wda_url: str = "http://localhost:8100",
285
+ ) -> str | None:
286
+ """
287
+ Get the device pasteboard (clipboard) content.
288
+
289
+ Args:
290
+ wda_url: WebDriverAgent URL.
291
+
292
+ Returns:
293
+ Pasteboard content or None if failed.
294
+ """
295
+ try:
296
+ import requests
297
+
298
+ url = f"{wda_url.rstrip('/')}/wda/getPasteboard"
299
+
300
+ response = requests.post(url, timeout=10, verify=False)
301
+
302
+ if response.status_code == 200:
303
+ data = response.json()
304
+ return data.get("value")
305
+
306
+ except ImportError:
307
+ print("Error: requests library required. Install: pip install requests")
308
+ except Exception as e:
309
+ print(f"Error getting pasteboard: {e}")
310
+
311
+ return None
@@ -0,0 +1,226 @@
1
+ """Screenshot utilities for capturing iOS device screen."""
2
+
3
+ import base64
4
+ import os
5
+ import subprocess
6
+ import tempfile
7
+ import uuid
8
+ from dataclasses import dataclass
9
+ from io import BytesIO
10
+
11
+ from PIL import Image
12
+
13
+
14
+ @dataclass
15
+ class Screenshot:
16
+ """Represents a captured screenshot."""
17
+
18
+ base64_data: str
19
+ width: int
20
+ height: int
21
+ is_sensitive: bool = False
22
+
23
+
24
+ def get_screenshot(
25
+ wda_url: str = "http://localhost:8100",
26
+ session_id: str | None = None,
27
+ device_id: str | None = None,
28
+ timeout: int = 10,
29
+ ) -> Screenshot:
30
+ """
31
+ Capture a screenshot from the connected iOS device.
32
+
33
+ Args:
34
+ wda_url: WebDriverAgent URL.
35
+ session_id: Optional WDA session ID.
36
+ device_id: Optional device UDID (for idevicescreenshot fallback).
37
+ timeout: Timeout in seconds for screenshot operations.
38
+
39
+ Returns:
40
+ Screenshot object containing base64 data and dimensions.
41
+
42
+ Note:
43
+ Tries WebDriverAgent first, falls back to idevicescreenshot if available.
44
+ If both fail, returns a black fallback image.
45
+ """
46
+ # Try WebDriverAgent first (preferred method)
47
+ screenshot = _get_screenshot_wda(wda_url, session_id, timeout)
48
+ if screenshot:
49
+ return screenshot
50
+
51
+ # Fallback to idevicescreenshot
52
+ screenshot = _get_screenshot_idevice(device_id, timeout)
53
+ if screenshot:
54
+ return screenshot
55
+
56
+ # Return fallback black image
57
+ return _create_fallback_screenshot(is_sensitive=False)
58
+
59
+
60
+ def _get_screenshot_wda(
61
+ wda_url: str, session_id: str | None, timeout: int
62
+ ) -> Screenshot | None:
63
+ """
64
+ Capture screenshot using WebDriverAgent.
65
+
66
+ Args:
67
+ wda_url: WebDriverAgent URL.
68
+ session_id: Optional WDA session ID.
69
+ timeout: Timeout in seconds.
70
+
71
+ Returns:
72
+ Screenshot object or None if failed.
73
+ """
74
+ try:
75
+ import requests
76
+
77
+ url = f"{wda_url.rstrip('/')}/screenshot"
78
+
79
+ response = requests.get(url, timeout=timeout, verify=False)
80
+
81
+ if response.status_code == 200:
82
+ data = response.json()
83
+ base64_data = data.get("value", "")
84
+
85
+ if base64_data:
86
+ # Decode to get dimensions
87
+ img_data = base64.b64decode(base64_data)
88
+ img = Image.open(BytesIO(img_data))
89
+ width, height = img.size
90
+
91
+ return Screenshot(
92
+ base64_data=base64_data,
93
+ width=width,
94
+ height=height,
95
+ is_sensitive=False,
96
+ )
97
+
98
+ except ImportError:
99
+ print("Note: requests library not installed. Install: pip install requests")
100
+ except Exception as e:
101
+ print(f"WDA screenshot failed: {e}")
102
+
103
+ return None
104
+
105
+
106
+ def _get_screenshot_idevice(device_id: str | None, timeout: int) -> Screenshot | None:
107
+ """
108
+ Capture screenshot using idevicescreenshot (libimobiledevice).
109
+
110
+ Args:
111
+ device_id: Optional device UDID.
112
+ timeout: Timeout in seconds.
113
+
114
+ Returns:
115
+ Screenshot object or None if failed.
116
+ """
117
+ try:
118
+ temp_path = os.path.join(
119
+ tempfile.gettempdir(), f"ios_screenshot_{uuid.uuid4()}.png"
120
+ )
121
+
122
+ cmd = ["idevicescreenshot"]
123
+ if device_id:
124
+ cmd.extend(["-u", device_id])
125
+ cmd.append(temp_path)
126
+
127
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
128
+
129
+ if result.returncode == 0 and os.path.exists(temp_path):
130
+ # Read and encode image
131
+ img = Image.open(temp_path)
132
+ width, height = img.size
133
+
134
+ buffered = BytesIO()
135
+ img.save(buffered, format="PNG")
136
+ base64_data = base64.b64encode(buffered.getvalue()).decode("utf-8")
137
+
138
+ # Cleanup
139
+ os.remove(temp_path)
140
+
141
+ return Screenshot(
142
+ base64_data=base64_data, width=width, height=height, is_sensitive=False
143
+ )
144
+
145
+ except FileNotFoundError:
146
+ print(
147
+ "Note: idevicescreenshot not found. Install: brew install libimobiledevice"
148
+ )
149
+ except Exception as e:
150
+ print(f"idevicescreenshot failed: {e}")
151
+
152
+ return None
153
+
154
+
155
+ def _create_fallback_screenshot(is_sensitive: bool) -> Screenshot:
156
+ """
157
+ Create a black fallback image when screenshot fails.
158
+
159
+ Args:
160
+ is_sensitive: Whether the failure was due to sensitive content.
161
+
162
+ Returns:
163
+ Screenshot object with black image.
164
+ """
165
+ # Default iPhone screen size (iPhone 14 Pro)
166
+ default_width, default_height = 1179, 2556
167
+
168
+ black_img = Image.new("RGB", (default_width, default_height), color="black")
169
+ buffered = BytesIO()
170
+ black_img.save(buffered, format="PNG")
171
+ base64_data = base64.b64encode(buffered.getvalue()).decode("utf-8")
172
+
173
+ return Screenshot(
174
+ base64_data=base64_data,
175
+ width=default_width,
176
+ height=default_height,
177
+ is_sensitive=is_sensitive,
178
+ )
179
+
180
+
181
+ def save_screenshot(
182
+ screenshot: Screenshot,
183
+ file_path: str,
184
+ ) -> bool:
185
+ """
186
+ Save a screenshot to a file.
187
+
188
+ Args:
189
+ screenshot: Screenshot object.
190
+ file_path: Path to save the screenshot.
191
+
192
+ Returns:
193
+ True if successful, False otherwise.
194
+ """
195
+ try:
196
+ img_data = base64.b64decode(screenshot.base64_data)
197
+ img = Image.open(BytesIO(img_data))
198
+ img.save(file_path)
199
+ return True
200
+ except Exception as e:
201
+ print(f"Error saving screenshot: {e}")
202
+ return False
203
+
204
+
205
+ def get_screenshot_png(
206
+ wda_url: str = "http://localhost:8100",
207
+ session_id: str | None = None,
208
+ device_id: str | None = None,
209
+ ) -> bytes | None:
210
+ """
211
+ Get screenshot as PNG bytes.
212
+
213
+ Args:
214
+ wda_url: WebDriverAgent URL.
215
+ session_id: Optional WDA session ID.
216
+ device_id: Optional device UDID.
217
+
218
+ Returns:
219
+ PNG bytes or None if failed.
220
+ """
221
+ screenshot = get_screenshot(wda_url, session_id, device_id)
222
+
223
+ try:
224
+ return base64.b64decode(screenshot.base64_data)
225
+ except Exception:
226
+ return None