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.
Files changed (135) hide show
  1. AutoGLM_GUI/__init__.py +11 -0
  2. AutoGLM_GUI/__main__.py +26 -4
  3. AutoGLM_GUI/actions/__init__.py +6 -0
  4. phone_agent/actions/handler_ios.py → AutoGLM_GUI/actions/handler.py +30 -112
  5. AutoGLM_GUI/actions/types.py +15 -0
  6. {phone_agent → AutoGLM_GUI}/adb/__init__.py +25 -23
  7. {phone_agent → AutoGLM_GUI}/adb/connection.py +5 -40
  8. {phone_agent → AutoGLM_GUI}/adb/device.py +12 -94
  9. {phone_agent → AutoGLM_GUI}/adb/input.py +6 -47
  10. AutoGLM_GUI/adb/screenshot.py +11 -0
  11. {phone_agent/config → AutoGLM_GUI/adb}/timing.py +1 -1
  12. AutoGLM_GUI/adb_plus/keyboard_installer.py +4 -2
  13. AutoGLM_GUI/adb_plus/screenshot.py +22 -1
  14. AutoGLM_GUI/adb_plus/serial.py +38 -20
  15. AutoGLM_GUI/adb_plus/touch.py +4 -9
  16. AutoGLM_GUI/agents/__init__.py +43 -12
  17. AutoGLM_GUI/agents/events.py +19 -0
  18. AutoGLM_GUI/agents/factory.py +31 -38
  19. AutoGLM_GUI/agents/glm/__init__.py +7 -0
  20. AutoGLM_GUI/agents/glm/agent.py +297 -0
  21. AutoGLM_GUI/agents/glm/message_builder.py +81 -0
  22. AutoGLM_GUI/agents/glm/parser.py +110 -0
  23. {phone_agent/config → AutoGLM_GUI/agents/glm}/prompts_en.py +7 -9
  24. {phone_agent/config → AutoGLM_GUI/agents/glm}/prompts_zh.py +18 -25
  25. AutoGLM_GUI/agents/mai/__init__.py +28 -0
  26. AutoGLM_GUI/agents/mai/agent.py +408 -0
  27. AutoGLM_GUI/agents/mai/parser.py +254 -0
  28. AutoGLM_GUI/agents/mai/prompts.py +103 -0
  29. AutoGLM_GUI/agents/mai/traj_memory.py +91 -0
  30. AutoGLM_GUI/agents/protocols.py +12 -8
  31. AutoGLM_GUI/agents/stream_runner.py +193 -0
  32. AutoGLM_GUI/api/__init__.py +40 -21
  33. AutoGLM_GUI/api/agents.py +181 -239
  34. AutoGLM_GUI/api/control.py +9 -6
  35. AutoGLM_GUI/api/devices.py +102 -12
  36. AutoGLM_GUI/api/history.py +104 -0
  37. AutoGLM_GUI/api/layered_agent.py +67 -15
  38. AutoGLM_GUI/api/media.py +64 -1
  39. AutoGLM_GUI/api/scheduled_tasks.py +98 -0
  40. AutoGLM_GUI/config.py +81 -0
  41. AutoGLM_GUI/config_manager.py +68 -51
  42. AutoGLM_GUI/device_manager.py +248 -29
  43. AutoGLM_GUI/device_protocol.py +1 -1
  44. AutoGLM_GUI/devices/adb_device.py +5 -10
  45. AutoGLM_GUI/devices/mock_device.py +4 -2
  46. AutoGLM_GUI/devices/remote_device.py +8 -3
  47. AutoGLM_GUI/history_manager.py +164 -0
  48. AutoGLM_GUI/model/__init__.py +5 -0
  49. AutoGLM_GUI/model/message_builder.py +69 -0
  50. AutoGLM_GUI/model/types.py +24 -0
  51. AutoGLM_GUI/models/__init__.py +10 -0
  52. AutoGLM_GUI/models/history.py +140 -0
  53. AutoGLM_GUI/models/scheduled_task.py +71 -0
  54. AutoGLM_GUI/parsers/__init__.py +22 -0
  55. AutoGLM_GUI/parsers/base.py +50 -0
  56. AutoGLM_GUI/parsers/phone_parser.py +58 -0
  57. AutoGLM_GUI/phone_agent_manager.py +62 -396
  58. AutoGLM_GUI/platform_utils.py +26 -0
  59. AutoGLM_GUI/prompt_config.py +15 -0
  60. AutoGLM_GUI/prompts/__init__.py +32 -0
  61. AutoGLM_GUI/scheduler_manager.py +350 -0
  62. AutoGLM_GUI/schemas.py +246 -72
  63. AutoGLM_GUI/scrcpy_stream.py +142 -24
  64. AutoGLM_GUI/socketio_server.py +100 -27
  65. AutoGLM_GUI/static/assets/{about-_XNhzQZX.js → about-CfwX1Cmc.js} +1 -1
  66. AutoGLM_GUI/static/assets/alert-dialog-CtGlN2IJ.js +1 -0
  67. AutoGLM_GUI/static/assets/chat-BYa-foUI.js +129 -0
  68. AutoGLM_GUI/static/assets/circle-alert-t08bEMPO.js +1 -0
  69. AutoGLM_GUI/static/assets/dialog-FNwZJFwk.js +45 -0
  70. AutoGLM_GUI/static/assets/eye-D0UPWCWC.js +1 -0
  71. AutoGLM_GUI/static/assets/history-CRo95B7i.js +1 -0
  72. AutoGLM_GUI/static/assets/{index-Cy8TmmHV.js → index-BaLMSqd3.js} +1 -1
  73. AutoGLM_GUI/static/assets/index-CTHbFvKl.js +11 -0
  74. AutoGLM_GUI/static/assets/index-CV7jGxGm.css +1 -0
  75. AutoGLM_GUI/static/assets/label-DJFevVmr.js +1 -0
  76. AutoGLM_GUI/static/assets/logs-RW09DyYY.js +1 -0
  77. AutoGLM_GUI/static/assets/popover--JTJrE5v.js +1 -0
  78. AutoGLM_GUI/static/assets/scheduled-tasks-DTRKsQXF.js +1 -0
  79. AutoGLM_GUI/static/assets/square-pen-CPK_K680.js +1 -0
  80. AutoGLM_GUI/static/assets/textarea-PRmVnWq5.js +1 -0
  81. AutoGLM_GUI/static/assets/workflows-CdcsAoaT.js +1 -0
  82. AutoGLM_GUI/static/index.html +2 -2
  83. AutoGLM_GUI/types.py +17 -0
  84. {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/METADATA +179 -130
  85. autoglm_gui-1.5.1.dist-info/RECORD +118 -0
  86. AutoGLM_GUI/agents/mai_adapter.py +0 -627
  87. AutoGLM_GUI/api/dual_model.py +0 -317
  88. AutoGLM_GUI/device_adapter.py +0 -263
  89. AutoGLM_GUI/dual_model/__init__.py +0 -53
  90. AutoGLM_GUI/dual_model/decision_model.py +0 -664
  91. AutoGLM_GUI/dual_model/dual_agent.py +0 -917
  92. AutoGLM_GUI/dual_model/protocols.py +0 -354
  93. AutoGLM_GUI/dual_model/vision_model.py +0 -442
  94. AutoGLM_GUI/mai_ui_adapter/agent_wrapper.py +0 -291
  95. AutoGLM_GUI/phone_agent_patches.py +0 -147
  96. AutoGLM_GUI/static/assets/chat-DwJpiAWf.js +0 -126
  97. AutoGLM_GUI/static/assets/dialog-B3uW4T8V.js +0 -45
  98. AutoGLM_GUI/static/assets/index-Cpv2gSF1.css +0 -1
  99. AutoGLM_GUI/static/assets/index-UYYauTly.js +0 -12
  100. AutoGLM_GUI/static/assets/workflows-Du_de-dt.js +0 -1
  101. autoglm_gui-1.4.1.dist-info/RECORD +0 -117
  102. mai_agent/base.py +0 -137
  103. mai_agent/mai_grounding_agent.py +0 -263
  104. mai_agent/mai_naivigation_agent.py +0 -526
  105. mai_agent/prompt.py +0 -148
  106. mai_agent/unified_memory.py +0 -67
  107. mai_agent/utils.py +0 -73
  108. phone_agent/__init__.py +0 -12
  109. phone_agent/actions/__init__.py +0 -5
  110. phone_agent/actions/handler.py +0 -400
  111. phone_agent/adb/screenshot.py +0 -108
  112. phone_agent/agent.py +0 -253
  113. phone_agent/agent_ios.py +0 -277
  114. phone_agent/config/__init__.py +0 -53
  115. phone_agent/config/apps_harmonyos.py +0 -256
  116. phone_agent/config/apps_ios.py +0 -339
  117. phone_agent/config/prompts.py +0 -80
  118. phone_agent/device_factory.py +0 -166
  119. phone_agent/hdc/__init__.py +0 -53
  120. phone_agent/hdc/connection.py +0 -384
  121. phone_agent/hdc/device.py +0 -269
  122. phone_agent/hdc/input.py +0 -145
  123. phone_agent/hdc/screenshot.py +0 -127
  124. phone_agent/model/__init__.py +0 -5
  125. phone_agent/model/client.py +0 -290
  126. phone_agent/xctest/__init__.py +0 -47
  127. phone_agent/xctest/connection.py +0 -379
  128. phone_agent/xctest/device.py +0 -472
  129. phone_agent/xctest/input.py +0 -311
  130. phone_agent/xctest/screenshot.py +0 -226
  131. {phone_agent/config → AutoGLM_GUI/adb}/apps.py +0 -0
  132. {phone_agent/config → AutoGLM_GUI}/i18n.py +0 -0
  133. {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/WHEEL +0 -0
  134. {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/entry_points.txt +0 -0
  135. {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,472 +0,0 @@
1
- """Device control utilities for iOS automation via WebDriverAgent."""
2
-
3
- import time
4
-
5
- from phone_agent.config.apps_ios import APP_PACKAGES_IOS as APP_PACKAGES
6
-
7
- SCALE_FACTOR = 3 # 3 for most modern iPhone
8
-
9
-
10
- def _get_wda_session_url(wda_url: str, session_id: str | None, endpoint: str) -> str:
11
- """
12
- Get the correct WDA URL for a session endpoint.
13
-
14
- Args:
15
- wda_url: Base WDA URL.
16
- session_id: Optional session ID.
17
- endpoint: The endpoint path.
18
-
19
- Returns:
20
- Full URL for the endpoint.
21
- """
22
- base = wda_url.rstrip("/")
23
- if session_id:
24
- return f"{base}/session/{session_id}/{endpoint}"
25
- else:
26
- # Try to use WDA endpoints without session when possible
27
- return f"{base}/{endpoint}"
28
-
29
-
30
- def get_current_app(
31
- wda_url: str = "http://localhost:8100", session_id: str | None = None
32
- ) -> str:
33
- """
34
- Get the currently active app bundle ID and name.
35
-
36
- Args:
37
- wda_url: WebDriverAgent URL.
38
- session_id: Optional WDA session ID.
39
-
40
- Returns:
41
- The app name if recognized, otherwise "System Home".
42
- """
43
- try:
44
- import requests
45
-
46
- # Get active app info from WDA using activeAppInfo endpoint
47
- response = requests.get(
48
- f"{wda_url.rstrip('/')}/wda/activeAppInfo", timeout=5, verify=False
49
- )
50
-
51
- if response.status_code == 200:
52
- data = response.json()
53
- # Extract bundle ID from response
54
- # Response format: {"value": {"bundleId": "com.apple.AppStore", "name": "", "pid": 825, "processArguments": {...}}, "sessionId": "..."}
55
- value = data.get("value", {})
56
- bundle_id = value.get("bundleId", "")
57
-
58
- if bundle_id:
59
- # Try to find app name from bundle ID
60
- for app_name, package in APP_PACKAGES.items():
61
- if package == bundle_id:
62
- return app_name
63
-
64
- return "System Home"
65
-
66
- except ImportError:
67
- print("Error: requests library required. Install: pip install requests")
68
- except Exception as e:
69
- print(f"Error getting current app: {e}")
70
-
71
- return "System Home"
72
-
73
-
74
- def tap(
75
- x: int,
76
- y: int,
77
- wda_url: str = "http://localhost:8100",
78
- session_id: str | None = None,
79
- delay: float = 1.0,
80
- ) -> None:
81
- """
82
- Tap at the specified coordinates using WebDriver W3C Actions API.
83
-
84
- Args:
85
- x: X coordinate.
86
- y: Y coordinate.
87
- wda_url: WebDriverAgent URL.
88
- session_id: Optional WDA session ID.
89
- delay: Delay in seconds after tap.
90
- """
91
- try:
92
- import requests
93
-
94
- url = _get_wda_session_url(wda_url, session_id, "actions")
95
-
96
- # W3C WebDriver Actions API for tap/click
97
- actions = {
98
- "actions": [
99
- {
100
- "type": "pointer",
101
- "id": "finger1",
102
- "parameters": {"pointerType": "touch"},
103
- "actions": [
104
- {
105
- "type": "pointerMove",
106
- "duration": 0,
107
- "x": x / SCALE_FACTOR,
108
- "y": y / SCALE_FACTOR,
109
- },
110
- {"type": "pointerDown", "button": 0},
111
- {"type": "pause", "duration": 0.1},
112
- {"type": "pointerUp", "button": 0},
113
- ],
114
- }
115
- ]
116
- }
117
-
118
- requests.post(url, json=actions, timeout=15, verify=False)
119
-
120
- time.sleep(delay)
121
-
122
- except ImportError:
123
- print("Error: requests library required. Install: pip install requests")
124
- except Exception as e:
125
- print(f"Error tapping: {e}")
126
-
127
-
128
- def double_tap(
129
- x: int,
130
- y: int,
131
- wda_url: str = "http://localhost:8100",
132
- session_id: str | None = None,
133
- delay: float = 1.0,
134
- ) -> None:
135
- """
136
- Double tap at the specified coordinates using WebDriver W3C Actions API.
137
-
138
- Args:
139
- x: X coordinate.
140
- y: Y coordinate.
141
- wda_url: WebDriverAgent URL.
142
- session_id: Optional WDA session ID.
143
- delay: Delay in seconds after double tap.
144
- """
145
- try:
146
- import requests
147
-
148
- url = _get_wda_session_url(wda_url, session_id, "actions")
149
-
150
- # W3C WebDriver Actions API for double tap
151
- actions = {
152
- "actions": [
153
- {
154
- "type": "pointer",
155
- "id": "finger1",
156
- "parameters": {"pointerType": "touch"},
157
- "actions": [
158
- {
159
- "type": "pointerMove",
160
- "duration": 0,
161
- "x": x / SCALE_FACTOR,
162
- "y": y / SCALE_FACTOR,
163
- },
164
- {"type": "pointerDown", "button": 0},
165
- {"type": "pause", "duration": 100},
166
- {"type": "pointerUp", "button": 0},
167
- {"type": "pause", "duration": 100},
168
- {"type": "pointerDown", "button": 0},
169
- {"type": "pause", "duration": 100},
170
- {"type": "pointerUp", "button": 0},
171
- ],
172
- }
173
- ]
174
- }
175
-
176
- requests.post(url, json=actions, timeout=10, verify=False)
177
-
178
- time.sleep(delay)
179
-
180
- except ImportError:
181
- print("Error: requests library required. Install: pip install requests")
182
- except Exception as e:
183
- print(f"Error double tapping: {e}")
184
-
185
-
186
- def long_press(
187
- x: int,
188
- y: int,
189
- duration: float = 3.0,
190
- wda_url: str = "http://localhost:8100",
191
- session_id: str | None = None,
192
- delay: float = 1.0,
193
- ) -> None:
194
- """
195
- Long press at the specified coordinates using WebDriver W3C Actions API.
196
-
197
- Args:
198
- x: X coordinate.
199
- y: Y coordinate.
200
- duration: Duration of press in seconds.
201
- wda_url: WebDriverAgent URL.
202
- session_id: Optional WDA session ID.
203
- delay: Delay in seconds after long press.
204
- """
205
- try:
206
- import requests
207
-
208
- url = _get_wda_session_url(wda_url, session_id, "actions")
209
-
210
- # W3C WebDriver Actions API for long press
211
- # Convert duration to milliseconds
212
- duration_ms = int(duration * 1000)
213
-
214
- actions = {
215
- "actions": [
216
- {
217
- "type": "pointer",
218
- "id": "finger1",
219
- "parameters": {"pointerType": "touch"},
220
- "actions": [
221
- {
222
- "type": "pointerMove",
223
- "duration": 0,
224
- "x": x / SCALE_FACTOR,
225
- "y": y / SCALE_FACTOR,
226
- },
227
- {"type": "pointerDown", "button": 0},
228
- {"type": "pause", "duration": duration_ms},
229
- {"type": "pointerUp", "button": 0},
230
- ],
231
- }
232
- ]
233
- }
234
-
235
- requests.post(url, json=actions, timeout=int(duration + 10), verify=False)
236
-
237
- time.sleep(delay)
238
-
239
- except ImportError:
240
- print("Error: requests library required. Install: pip install requests")
241
- except Exception as e:
242
- print(f"Error long pressing: {e}")
243
-
244
-
245
- def swipe(
246
- start_x: int,
247
- start_y: int,
248
- end_x: int,
249
- end_y: int,
250
- duration: float | None = None,
251
- wda_url: str = "http://localhost:8100",
252
- session_id: str | None = None,
253
- delay: float = 1.0,
254
- ) -> None:
255
- """
256
- Swipe from start to end coordinates using WDA dragfromtoforduration endpoint.
257
-
258
- Args:
259
- start_x: Starting X coordinate.
260
- start_y: Starting Y coordinate.
261
- end_x: Ending X coordinate.
262
- end_y: Ending Y coordinate.
263
- duration: Duration of swipe in seconds (auto-calculated if None).
264
- wda_url: WebDriverAgent URL.
265
- session_id: Optional WDA session ID.
266
- delay: Delay in seconds after swipe.
267
- """
268
- try:
269
- import requests
270
-
271
- if duration is None:
272
- # Calculate duration based on distance
273
- dist_sq = (start_x - end_x) ** 2 + (start_y - end_y) ** 2
274
- duration = dist_sq / 1000000 # Convert to seconds
275
- duration = max(0.3, min(duration, 2.0)) # Clamp between 0.3-2 seconds
276
-
277
- url = _get_wda_session_url(wda_url, session_id, "wda/dragfromtoforduration")
278
-
279
- # WDA dragfromtoforduration API payload
280
- payload = {
281
- "fromX": start_x / SCALE_FACTOR,
282
- "fromY": start_y / SCALE_FACTOR,
283
- "toX": end_x / SCALE_FACTOR,
284
- "toY": end_y / SCALE_FACTOR,
285
- "duration": duration,
286
- }
287
-
288
- requests.post(url, json=payload, timeout=int(duration + 10), verify=False)
289
-
290
- time.sleep(delay)
291
-
292
- except ImportError:
293
- print("Error: requests library required. Install: pip install requests")
294
- except Exception as e:
295
- print(f"Error swiping: {e}")
296
-
297
-
298
- def back(
299
- wda_url: str = "http://localhost:8100",
300
- session_id: str | None = None,
301
- delay: float = 1.0,
302
- ) -> None:
303
- """
304
- Navigate back (swipe from left edge).
305
-
306
- Args:
307
- wda_url: WebDriverAgent URL.
308
- session_id: Optional WDA session ID.
309
- delay: Delay in seconds after navigation.
310
-
311
- Note:
312
- iOS doesn't have a universal back button. This simulates a back gesture
313
- by swiping from the left edge of the screen.
314
- """
315
- try:
316
- import requests
317
-
318
- url = _get_wda_session_url(wda_url, session_id, "wda/dragfromtoforduration")
319
-
320
- # Swipe from left edge to simulate back gesture
321
- payload = {
322
- "fromX": 0,
323
- "fromY": 640,
324
- "toX": 400,
325
- "toY": 640,
326
- "duration": 0.3,
327
- }
328
-
329
- requests.post(url, json=payload, timeout=10, verify=False)
330
-
331
- time.sleep(delay)
332
-
333
- except ImportError:
334
- print("Error: requests library required. Install: pip install requests")
335
- except Exception as e:
336
- print(f"Error performing back gesture: {e}")
337
-
338
-
339
- def home(
340
- wda_url: str = "http://localhost:8100",
341
- session_id: str | None = None,
342
- delay: float = 1.0,
343
- ) -> None:
344
- """
345
- Press the home button.
346
-
347
- Args:
348
- wda_url: WebDriverAgent URL.
349
- session_id: Optional WDA session ID.
350
- delay: Delay in seconds after pressing home.
351
- """
352
- try:
353
- import requests
354
-
355
- url = f"{wda_url.rstrip('/')}/wda/homescreen"
356
-
357
- requests.post(url, timeout=10, verify=False)
358
-
359
- time.sleep(delay)
360
-
361
- except ImportError:
362
- print("Error: requests library required. Install: pip install requests")
363
- except Exception as e:
364
- print(f"Error pressing home: {e}")
365
-
366
-
367
- def launch_app(
368
- app_name: str,
369
- wda_url: str = "http://localhost:8100",
370
- session_id: str | None = None,
371
- delay: float = 1.0,
372
- ) -> bool:
373
- """
374
- Launch an app by name.
375
-
376
- Args:
377
- app_name: The app name (must be in APP_PACKAGES).
378
- wda_url: WebDriverAgent URL.
379
- session_id: Optional WDA session ID.
380
- delay: Delay in seconds after launching.
381
-
382
- Returns:
383
- True if app was launched, False if app not found.
384
- """
385
- if app_name not in APP_PACKAGES:
386
- return False
387
-
388
- try:
389
- import requests
390
-
391
- bundle_id = APP_PACKAGES[app_name]
392
- url = _get_wda_session_url(wda_url, session_id, "wda/apps/launch")
393
-
394
- response = requests.post(
395
- url, json={"bundleId": bundle_id}, timeout=10, verify=False
396
- )
397
-
398
- time.sleep(delay)
399
- return response.status_code in (200, 201)
400
-
401
- except ImportError:
402
- print("Error: requests library required. Install: pip install requests")
403
- return False
404
- except Exception as e:
405
- print(f"Error launching app: {e}")
406
- return False
407
-
408
-
409
- def get_screen_size(
410
- wda_url: str = "http://localhost:8100", session_id: str | None = None
411
- ) -> tuple[int, int]:
412
- """
413
- Get the screen dimensions.
414
-
415
- Args:
416
- wda_url: WebDriverAgent URL.
417
- session_id: Optional WDA session ID.
418
-
419
- Returns:
420
- Tuple of (width, height). Returns (375, 812) as default if unable to fetch.
421
- """
422
- try:
423
- import requests
424
-
425
- url = _get_wda_session_url(wda_url, session_id, "window/size")
426
-
427
- response = requests.get(url, timeout=5, verify=False)
428
-
429
- if response.status_code == 200:
430
- data = response.json()
431
- value = data.get("value", {})
432
- width = value.get("width", 375)
433
- height = value.get("height", 812)
434
- return width, height
435
-
436
- except ImportError:
437
- print("Error: requests library required. Install: pip install requests")
438
- except Exception as e:
439
- print(f"Error getting screen size: {e}")
440
-
441
- # Default iPhone screen size (iPhone X and later)
442
- return 375, 812
443
-
444
-
445
- def press_button(
446
- button_name: str,
447
- wda_url: str = "http://localhost:8100",
448
- session_id: str | None = None,
449
- delay: float = 1.0,
450
- ) -> None:
451
- """
452
- Press a physical button.
453
-
454
- Args:
455
- button_name: Button name (e.g., "home", "volumeUp", "volumeDown").
456
- wda_url: WebDriverAgent URL.
457
- session_id: Optional WDA session ID.
458
- delay: Delay in seconds after pressing.
459
- """
460
- try:
461
- import requests
462
-
463
- url = f"{wda_url.rstrip('/')}/wda/pressButton"
464
-
465
- requests.post(url, json={"name": button_name}, timeout=10, verify=False)
466
-
467
- time.sleep(delay)
468
-
469
- except ImportError:
470
- print("Error: requests library required. Install: pip install requests")
471
- except Exception as e:
472
- print(f"Error pressing button: {e}")