lybic-guiagents 0.1.0__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.

Potentially problematic release.


This version of lybic-guiagents might be problematic. Click here for more details.

Files changed (85) hide show
  1. desktop_env/__init__.py +1 -0
  2. desktop_env/actions.py +203 -0
  3. desktop_env/controllers/__init__.py +0 -0
  4. desktop_env/controllers/python.py +471 -0
  5. desktop_env/controllers/setup.py +882 -0
  6. desktop_env/desktop_env.py +509 -0
  7. desktop_env/evaluators/__init__.py +5 -0
  8. desktop_env/evaluators/getters/__init__.py +41 -0
  9. desktop_env/evaluators/getters/calc.py +15 -0
  10. desktop_env/evaluators/getters/chrome.py +1774 -0
  11. desktop_env/evaluators/getters/file.py +154 -0
  12. desktop_env/evaluators/getters/general.py +42 -0
  13. desktop_env/evaluators/getters/gimp.py +38 -0
  14. desktop_env/evaluators/getters/impress.py +126 -0
  15. desktop_env/evaluators/getters/info.py +24 -0
  16. desktop_env/evaluators/getters/misc.py +406 -0
  17. desktop_env/evaluators/getters/replay.py +20 -0
  18. desktop_env/evaluators/getters/vlc.py +86 -0
  19. desktop_env/evaluators/getters/vscode.py +35 -0
  20. desktop_env/evaluators/metrics/__init__.py +160 -0
  21. desktop_env/evaluators/metrics/basic_os.py +68 -0
  22. desktop_env/evaluators/metrics/chrome.py +493 -0
  23. desktop_env/evaluators/metrics/docs.py +1011 -0
  24. desktop_env/evaluators/metrics/general.py +665 -0
  25. desktop_env/evaluators/metrics/gimp.py +637 -0
  26. desktop_env/evaluators/metrics/libreoffice.py +28 -0
  27. desktop_env/evaluators/metrics/others.py +92 -0
  28. desktop_env/evaluators/metrics/pdf.py +31 -0
  29. desktop_env/evaluators/metrics/slides.py +957 -0
  30. desktop_env/evaluators/metrics/table.py +585 -0
  31. desktop_env/evaluators/metrics/thunderbird.py +176 -0
  32. desktop_env/evaluators/metrics/utils.py +719 -0
  33. desktop_env/evaluators/metrics/vlc.py +524 -0
  34. desktop_env/evaluators/metrics/vscode.py +283 -0
  35. desktop_env/providers/__init__.py +35 -0
  36. desktop_env/providers/aws/__init__.py +0 -0
  37. desktop_env/providers/aws/manager.py +278 -0
  38. desktop_env/providers/aws/provider.py +186 -0
  39. desktop_env/providers/aws/provider_with_proxy.py +315 -0
  40. desktop_env/providers/aws/proxy_pool.py +193 -0
  41. desktop_env/providers/azure/__init__.py +0 -0
  42. desktop_env/providers/azure/manager.py +87 -0
  43. desktop_env/providers/azure/provider.py +207 -0
  44. desktop_env/providers/base.py +97 -0
  45. desktop_env/providers/gcp/__init__.py +0 -0
  46. desktop_env/providers/gcp/manager.py +0 -0
  47. desktop_env/providers/gcp/provider.py +0 -0
  48. desktop_env/providers/virtualbox/__init__.py +0 -0
  49. desktop_env/providers/virtualbox/manager.py +463 -0
  50. desktop_env/providers/virtualbox/provider.py +124 -0
  51. desktop_env/providers/vmware/__init__.py +0 -0
  52. desktop_env/providers/vmware/manager.py +455 -0
  53. desktop_env/providers/vmware/provider.py +105 -0
  54. gui_agents/__init__.py +0 -0
  55. gui_agents/agents/Action.py +209 -0
  56. gui_agents/agents/__init__.py +0 -0
  57. gui_agents/agents/agent_s.py +832 -0
  58. gui_agents/agents/global_state.py +610 -0
  59. gui_agents/agents/grounding.py +651 -0
  60. gui_agents/agents/hardware_interface.py +129 -0
  61. gui_agents/agents/manager.py +568 -0
  62. gui_agents/agents/translator.py +132 -0
  63. gui_agents/agents/worker.py +355 -0
  64. gui_agents/cli_app.py +560 -0
  65. gui_agents/core/__init__.py +0 -0
  66. gui_agents/core/engine.py +1496 -0
  67. gui_agents/core/knowledge.py +449 -0
  68. gui_agents/core/mllm.py +555 -0
  69. gui_agents/tools/__init__.py +0 -0
  70. gui_agents/tools/tools.py +727 -0
  71. gui_agents/unit_test/__init__.py +0 -0
  72. gui_agents/unit_test/run_tests.py +65 -0
  73. gui_agents/unit_test/test_manager.py +330 -0
  74. gui_agents/unit_test/test_worker.py +269 -0
  75. gui_agents/utils/__init__.py +0 -0
  76. gui_agents/utils/analyze_display.py +301 -0
  77. gui_agents/utils/common_utils.py +263 -0
  78. gui_agents/utils/display_viewer.py +281 -0
  79. gui_agents/utils/embedding_manager.py +53 -0
  80. gui_agents/utils/image_axis_utils.py +27 -0
  81. lybic_guiagents-0.1.0.dist-info/METADATA +416 -0
  82. lybic_guiagents-0.1.0.dist-info/RECORD +85 -0
  83. lybic_guiagents-0.1.0.dist-info/WHEEL +5 -0
  84. lybic_guiagents-0.1.0.dist-info/licenses/LICENSE +201 -0
  85. lybic_guiagents-0.1.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,186 @@
1
+ import boto3
2
+ from botocore.exceptions import ClientError
3
+
4
+ import logging
5
+
6
+ from desktop_env.providers.base import Provider
7
+ from datetime import datetime
8
+ import time
9
+
10
+
11
+ logger = logging.getLogger("desktopenv.providers.aws.AWSProvider")
12
+ logger.setLevel(logging.INFO)
13
+
14
+ WAIT_DELAY = 15
15
+ MAX_ATTEMPTS = 10
16
+
17
+
18
+ class AWSProvider(Provider):
19
+
20
+
21
+ def start_emulator(self, path_to_vm: str, headless: bool, *args, **kwargs):
22
+ logger.info("Starting AWS VM...")
23
+ ec2_client = boto3.client('ec2', region_name=self.region)
24
+
25
+ try:
26
+ # Check the current state of the instance
27
+ response = ec2_client.describe_instances(InstanceIds=[path_to_vm])
28
+ state = response['Reservations'][0]['Instances'][0]['State']['Name']
29
+ logger.info(f"Instance {path_to_vm} current state: {state}")
30
+
31
+ if state == 'running':
32
+ # If the instance is already running, skip starting it
33
+ logger.info(f"Instance {path_to_vm} is already running. Skipping start.")
34
+ return
35
+
36
+ if state == 'stopped':
37
+ # Start the instance if it's currently stopped
38
+ ec2_client.start_instances(InstanceIds=[path_to_vm])
39
+ logger.info(f"Instance {path_to_vm} is starting...")
40
+
41
+ # Wait until the instance reaches 'running' state
42
+ waiter = ec2_client.get_waiter('instance_running')
43
+ waiter.wait(
44
+ InstanceIds=[path_to_vm],
45
+ WaiterConfig={'Delay': WAIT_DELAY, 'MaxAttempts': MAX_ATTEMPTS}
46
+ )
47
+ logger.info(f"Instance {path_to_vm} is now running.")
48
+ else:
49
+ # For all other states (terminated, pending, etc.), log a warning
50
+ logger.warning(f"Instance {path_to_vm} is in state '{state}' and cannot be started.")
51
+
52
+ except ClientError as e:
53
+ logger.error(f"Failed to start the AWS VM {path_to_vm}: {str(e)}")
54
+ raise
55
+
56
+
57
+ def get_ip_address(self, path_to_vm: str) -> str:
58
+ logger.info("Getting AWS VM IP address...")
59
+ ec2_client = boto3.client('ec2', region_name=self.region)
60
+
61
+ try:
62
+ response = ec2_client.describe_instances(InstanceIds=[path_to_vm])
63
+ for reservation in response['Reservations']:
64
+ for instance in reservation['Instances']:
65
+ private_ip_address = instance.get('PrivateIpAddress', '')
66
+ public_ip_address = instance.get('PublicIpAddress', '')
67
+
68
+ if public_ip_address:
69
+ vnc_url = f"http://{public_ip_address}:5910/vnc.html"
70
+ logger.info("="*80)
71
+ logger.info(f"๐Ÿ–ฅ๏ธ VNC Web Access URL: {vnc_url}")
72
+ logger.info(f"๐Ÿ“ก Public IP: {public_ip_address}")
73
+ logger.info(f"๐Ÿ  Private IP: {private_ip_address}")
74
+ logger.info("="*80)
75
+ print(f"\n๐ŸŒ VNC Web Access URL: {vnc_url}")
76
+ print(f"๐Ÿ“ Please open the above address in the browser for remote desktop access\n")
77
+ else:
78
+ logger.warning("No public IP address available for VNC access")
79
+
80
+ return private_ip_address
81
+ return '' # Return an empty string if no IP address is found
82
+ except ClientError as e:
83
+ logger.error(f"Failed to retrieve IP address for the instance {path_to_vm}: {str(e)}")
84
+ raise
85
+
86
+ def save_state(self, path_to_vm: str, snapshot_name: str):
87
+ logger.info("Saving AWS VM state...")
88
+ ec2_client = boto3.client('ec2', region_name=self.region)
89
+
90
+ try:
91
+ image_response = ec2_client.create_image(InstanceId=path_to_vm, Name=snapshot_name)
92
+ image_id = image_response['ImageId']
93
+ logger.info(f"AMI {image_id} created successfully from instance {path_to_vm}.")
94
+ return image_id
95
+ except ClientError as e:
96
+ logger.error(f"Failed to create AMI from the instance {path_to_vm}: {str(e)}")
97
+ raise
98
+
99
+ def revert_to_snapshot(self, path_to_vm: str, snapshot_name: str):
100
+ logger.info(f"Reverting AWS VM to snapshot AMI: {snapshot_name}...")
101
+ ec2_client = boto3.client('ec2', region_name=self.region)
102
+
103
+ try:
104
+ # Step 1: Retrieve the original instance details
105
+ instance_details = ec2_client.describe_instances(InstanceIds=[path_to_vm])
106
+ instance = instance_details['Reservations'][0]['Instances'][0]
107
+ security_groups = [sg['GroupId'] for sg in instance['SecurityGroups']]
108
+ subnet_id = instance['SubnetId']
109
+ instance_type = instance['InstanceType']
110
+
111
+ # Step 2: Terminate the old instance
112
+ ec2_client.terminate_instances(InstanceIds=[path_to_vm])
113
+ logger.info(f"Old instance {path_to_vm} has been terminated.")
114
+
115
+ # Step 3: Launch a new instance from the snapshot(AMI) with performance optimization
116
+ logger.info(f"Launching a new instance from AMI {snapshot_name}...")
117
+
118
+ run_instances_params = {
119
+ "MaxCount": 1,
120
+ "MinCount": 1,
121
+ "ImageId": snapshot_name,
122
+ "InstanceType": instance_type,
123
+ "EbsOptimized": True,
124
+ "NetworkInterfaces": [
125
+ {
126
+ "SubnetId": subnet_id,
127
+ "AssociatePublicIpAddress": True,
128
+ "DeviceIndex": 0,
129
+ "Groups": security_groups
130
+ }
131
+ ],
132
+ "BlockDeviceMappings": [
133
+ {
134
+ "DeviceName": "/dev/sda1",
135
+ "Ebs": {
136
+ # "VolumeInitializationRate": 300
137
+ "VolumeSize": 30, # Size in GB
138
+ "VolumeType": "gp3", # General Purpose SSD
139
+ "Throughput": 1000,
140
+ "Iops": 4000 # Adjust IOPS as needed
141
+ }
142
+ }
143
+ ]
144
+ }
145
+
146
+ new_instance = ec2_client.run_instances(**run_instances_params)
147
+ new_instance_id = new_instance['Instances'][0]['InstanceId']
148
+ logger.info(f"New instance {new_instance_id} launched from AMI {snapshot_name}.")
149
+ logger.info(f"Waiting for instance {new_instance_id} to be running...")
150
+ ec2_client.get_waiter('instance_running').wait(InstanceIds=[new_instance_id])
151
+
152
+ logger.info(f"Instance {new_instance_id} is ready.")
153
+
154
+ try:
155
+ instance_details = ec2_client.describe_instances(InstanceIds=[new_instance_id])
156
+ instance = instance_details['Reservations'][0]['Instances'][0]
157
+ public_ip = instance.get('PublicIpAddress', '')
158
+ if public_ip:
159
+ vnc_url = f"http://{public_ip}:5910/vnc.html"
160
+ logger.info("="*80)
161
+ logger.info(f"๐Ÿ–ฅ๏ธ New Instance VNC Web Access URL: {vnc_url}")
162
+ logger.info(f"๐Ÿ“ก Public IP: {public_ip}")
163
+ logger.info(f"๐Ÿ†” New Instance ID: {new_instance_id}")
164
+ logger.info("="*80)
165
+ print(f"\n๐ŸŒ New Instance VNC Web Access URL: {vnc_url}")
166
+ print(f"๐Ÿ“ Please open the above address in the browser for remote desktop access\n")
167
+ except Exception as e:
168
+ logger.warning(f"Failed to get VNC address for new instance {new_instance_id}: {e}")
169
+
170
+ return new_instance_id
171
+
172
+ except ClientError as e:
173
+ logger.error(f"Failed to revert to snapshot {snapshot_name} for the instance {path_to_vm}: {str(e)}")
174
+ raise
175
+
176
+
177
+ def stop_emulator(self, path_to_vm, region=None):
178
+ logger.info(f"Stopping AWS VM {path_to_vm}...")
179
+ ec2_client = boto3.client('ec2', region_name=self.region)
180
+
181
+ try:
182
+ ec2_client.terminate_instances(InstanceIds=[path_to_vm])
183
+ logger.info(f"Instance {path_to_vm} has been terminated.")
184
+ except ClientError as e:
185
+ logger.error(f"Failed to stop the AWS VM {path_to_vm}: {str(e)}")
186
+ raise
@@ -0,0 +1,315 @@
1
+ import boto3
2
+ from botocore.exceptions import ClientError
3
+ import base64
4
+ import logging
5
+ import json
6
+ from typing import Optional
7
+
8
+ from desktop_env.providers.base import Provider
9
+ from desktop_env.providers.aws.proxy_pool import get_global_proxy_pool, init_proxy_pool, ProxyInfo
10
+
11
+ logger = logging.getLogger("desktopenv.providers.aws.AWSProviderWithProxy")
12
+ logger.setLevel(logging.INFO)
13
+
14
+ WAIT_DELAY = 15
15
+ MAX_ATTEMPTS = 10
16
+
17
+
18
+ class AWSProviderWithProxy(Provider):
19
+
20
+ def __init__(self, region: str = None, proxy_config_file: str = None):
21
+ super().__init__(region)
22
+ self.current_proxy: Optional[ProxyInfo] = None
23
+
24
+ # ๅˆๅง‹ๅŒ–ไปฃ็†ๆฑ 
25
+ if proxy_config_file:
26
+ init_proxy_pool(proxy_config_file)
27
+ logger.info(f"Initialized proxy pool from {proxy_config_file}")
28
+
29
+ # ่Žทๅ–ไธ‹ไธ€ไธชๅฏ็”จไปฃ็†
30
+ self._rotate_proxy()
31
+
32
+ def _rotate_proxy(self):
33
+ """่ฝฎๆขๅˆฐไธ‹ไธ€ไธชๅฏ็”จไปฃ็†"""
34
+ proxy_pool = get_global_proxy_pool()
35
+ self.current_proxy = proxy_pool.get_next_proxy()
36
+
37
+ if self.current_proxy:
38
+ logger.info(f"Switched to proxy: {self.current_proxy.host}:{self.current_proxy.port}")
39
+ else:
40
+ logger.warning("No proxy available, using direct connection")
41
+
42
+ def _generate_proxy_user_data(self) -> str:
43
+ """็”ŸๆˆๅŒ…ๅซไปฃ็†้…็ฝฎ็š„user data่„šๆœฌ"""
44
+ if not self.current_proxy:
45
+ return ""
46
+
47
+ proxy_url = self._format_proxy_url(self.current_proxy)
48
+
49
+ user_data_script = f"""#!/bin/bash
50
+ # Configure system proxy
51
+ echo 'export http_proxy={proxy_url}' >> /etc/environment
52
+ echo 'export https_proxy={proxy_url}' >> /etc/environment
53
+ echo 'export HTTP_PROXY={proxy_url}' >> /etc/environment
54
+ echo 'export HTTPS_PROXY={proxy_url}' >> /etc/environment
55
+
56
+ # Configure apt proxy
57
+ cat > /etc/apt/apt.conf.d/95proxy << EOF
58
+ Acquire::http::Proxy "{proxy_url}";
59
+ Acquire::https::Proxy "{proxy_url}";
60
+ EOF
61
+
62
+ # Configure chrome/chromium proxy
63
+ mkdir -p /etc/opt/chrome/policies/managed
64
+ cat > /etc/opt/chrome/policies/managed/proxy.json << EOF
65
+ {{
66
+ "ProxyMode": "fixed_servers",
67
+ "ProxyServer": "{self.current_proxy.host}:{self.current_proxy.port}"
68
+ }}
69
+ EOF
70
+
71
+ # Configure chromium proxy (Ubuntu default)
72
+ mkdir -p /etc/chromium/policies/managed
73
+ cat > /etc/chromium/policies/managed/proxy.json << EOF
74
+ {{
75
+ "ProxyMode": "fixed_servers",
76
+ "ProxyServer": "{self.current_proxy.host}:{self.current_proxy.port}"
77
+ }}
78
+ EOF
79
+
80
+ # Configure firefox proxy - support multiple possible paths
81
+ for firefox_dir in /etc/firefox/policies /usr/lib/firefox/distribution/policies /etc/firefox-esr/policies; do
82
+ if [ -d "$(dirname "$firefox_dir")" ]; then
83
+ mkdir -p "$firefox_dir"
84
+ cat > "$firefox_dir/policies.json" << EOF
85
+ {{
86
+ "policies": {{
87
+ "Proxy": {{
88
+ "Mode": "manual",
89
+ "HTTPProxy": "{self.current_proxy.host}:{self.current_proxy.port}",
90
+ "HTTPSProxy": "{self.current_proxy.host}:{self.current_proxy.port}",
91
+ "UseHTTPProxyForAllProtocols": true
92
+ }}
93
+ }}
94
+ }}
95
+ EOF
96
+ break
97
+ fi
98
+ done
99
+
100
+ # Reload environment variables
101
+ source /etc/environment
102
+
103
+ # Log proxy configuration
104
+ echo "$(date): Configured proxy {self.current_proxy.host}:{self.current_proxy.port}" >> /var/log/proxy-setup.log
105
+ """
106
+
107
+ return base64.b64encode(user_data_script.encode()).decode()
108
+
109
+ def _format_proxy_url(self, proxy: ProxyInfo) -> str:
110
+ """ๆ ผๅผๅŒ–ไปฃ็†URL"""
111
+ if proxy.username and proxy.password:
112
+ return f"{proxy.protocol}://{proxy.username}:{proxy.password}@{proxy.host}:{proxy.port}"
113
+ else:
114
+ return f"{proxy.protocol}://{proxy.host}:{proxy.port}"
115
+
116
+ def start_emulator(self, path_to_vm: str, headless: bool, *args, **kwargs):
117
+ logger.info("Starting AWS VM with proxy configuration...")
118
+ ec2_client = boto3.client('ec2', region_name=self.region)
119
+
120
+ try:
121
+ # ๅฆ‚ๆžœๅฎžไพ‹ๅทฒ็ปๅญ˜ๅœจ๏ผŒ็›ดๆŽฅๅฏๅŠจ
122
+ ec2_client.start_instances(InstanceIds=[path_to_vm])
123
+ logger.info(f"Instance {path_to_vm} is starting...")
124
+
125
+ # Wait for the instance to be in the 'running' state
126
+ waiter = ec2_client.get_waiter('instance_running')
127
+ waiter.wait(InstanceIds=[path_to_vm], WaiterConfig={'Delay': WAIT_DELAY, 'MaxAttempts': MAX_ATTEMPTS})
128
+ logger.info(f"Instance {path_to_vm} is now running.")
129
+
130
+ except ClientError as e:
131
+ logger.error(f"Failed to start the AWS VM {path_to_vm}: {str(e)}")
132
+ raise
133
+
134
+ def create_instance_with_proxy(self, image_id: str, instance_type: str,
135
+ security_groups: list, subnet_id: str) -> str:
136
+ """ๅˆ›ๅปบๅธฆๆœ‰ไปฃ็†้…็ฝฎ็š„ๆ–ฐๅฎžไพ‹"""
137
+ ec2_client = boto3.client('ec2', region_name=self.region)
138
+
139
+ user_data = self._generate_proxy_user_data()
140
+
141
+ run_instances_params = {
142
+ "MaxCount": 1,
143
+ "MinCount": 1,
144
+ "ImageId": image_id,
145
+ "InstanceType": instance_type,
146
+ "EbsOptimized": True,
147
+ "NetworkInterfaces": [
148
+ {
149
+ "SubnetId": subnet_id,
150
+ "AssociatePublicIpAddress": True,
151
+ "DeviceIndex": 0,
152
+ "Groups": security_groups
153
+ }
154
+ ]
155
+ }
156
+
157
+ if user_data:
158
+ run_instances_params["UserData"] = user_data
159
+
160
+ try:
161
+ response = ec2_client.run_instances(**run_instances_params)
162
+ instance_id = response['Instances'][0]['InstanceId']
163
+
164
+ logger.info(f"Created new instance {instance_id} with proxy configuration")
165
+
166
+ logger.info(f"Waiting for instance {instance_id} to be running...")
167
+ ec2_client.get_waiter('instance_running').wait(InstanceIds=[instance_id])
168
+ logger.info(f"Instance {instance_id} is ready.")
169
+
170
+ try:
171
+ instance_details = ec2_client.describe_instances(InstanceIds=[instance_id])
172
+ instance = instance_details['Reservations'][0]['Instances'][0]
173
+ public_ip = instance.get('PublicIpAddress', '')
174
+ if public_ip:
175
+ vnc_url = f"http://{public_ip}:5910/vnc.html"
176
+ logger.info("="*80)
177
+ logger.info(f"๐Ÿ–ฅ๏ธ VNC Web Access URL: {vnc_url}")
178
+ logger.info(f"๐Ÿ“ก Public IP: {public_ip}")
179
+ logger.info(f"๐Ÿ†” Instance ID: {instance_id}")
180
+ if self.current_proxy:
181
+ logger.info(f"๐ŸŒ Proxy: {self.current_proxy.host}:{self.current_proxy.port}")
182
+ logger.info("="*80)
183
+ print(f"\n๐ŸŒ VNC Web Access URL: {vnc_url}")
184
+ if self.current_proxy:
185
+ print(f"๐Ÿ”„ Current Proxy: {self.current_proxy.host}:{self.current_proxy.port}")
186
+ print(f"๐Ÿ“ Please open the above address in the browser for remote desktop access\n")
187
+ except Exception as e:
188
+ logger.warning(f"Failed to get VNC address for instance {instance_id}: {e}")
189
+
190
+ return instance_id
191
+
192
+ except ClientError as e:
193
+ logger.error(f"Failed to create instance with proxy: {str(e)}")
194
+ if self.current_proxy:
195
+ proxy_pool = get_global_proxy_pool()
196
+ proxy_pool.mark_proxy_failed(self.current_proxy)
197
+ self._rotate_proxy()
198
+ raise
199
+
200
+ def get_ip_address(self, path_to_vm: str) -> str:
201
+ logger.info("Getting AWS VM IP address...")
202
+ ec2_client = boto3.client('ec2', region_name=self.region)
203
+
204
+ try:
205
+ response = ec2_client.describe_instances(InstanceIds=[path_to_vm])
206
+ for reservation in response['Reservations']:
207
+ for instance in reservation['Instances']:
208
+ private_ip_address = instance.get('PrivateIpAddress', '')
209
+ public_ip_address = instance.get('PublicIpAddress', '')
210
+
211
+ if public_ip_address:
212
+ vnc_url = f"http://{public_ip_address}:5910/vnc.html"
213
+ logger.info("="*80)
214
+ logger.info(f"๐Ÿ–ฅ๏ธ VNC Web Access URL: {vnc_url}")
215
+ logger.info(f"๐Ÿ“ก Public IP: {public_ip_address}")
216
+ logger.info(f"๐Ÿ  Private IP: {private_ip_address}")
217
+ if self.current_proxy:
218
+ logger.info(f"๐ŸŒ Proxy: {self.current_proxy.host}:{self.current_proxy.port}")
219
+ logger.info("="*80)
220
+ print(f"\n๐ŸŒ VNC Web Access URL: {vnc_url}")
221
+ if self.current_proxy:
222
+ print(f"๐Ÿ”„ Current Proxy: {self.current_proxy.host}:{self.current_proxy.port}")
223
+ print(f"๐Ÿ“ Please open the above address in the browser for remote desktop access\n")
224
+ else:
225
+ logger.warning("No public IP address available for VNC access")
226
+
227
+ return private_ip_address
228
+ return ''
229
+ except ClientError as e:
230
+ logger.error(f"Failed to retrieve IP address for the instance {path_to_vm}: {str(e)}")
231
+ raise
232
+
233
+ def save_state(self, path_to_vm: str, snapshot_name: str):
234
+ logger.info("Saving AWS VM state...")
235
+ ec2_client = boto3.client('ec2', region_name=self.region)
236
+
237
+ try:
238
+ image_response = ec2_client.create_image(InstanceId=path_to_vm, Name=snapshot_name)
239
+ image_id = image_response['ImageId']
240
+ logger.info(f"AMI {image_id} created successfully from instance {path_to_vm}.")
241
+ return image_id
242
+ except ClientError as e:
243
+ logger.error(f"Failed to create AMI from the instance {path_to_vm}: {str(e)}")
244
+ raise
245
+
246
+ def revert_to_snapshot(self, path_to_vm: str, snapshot_name: str):
247
+ logger.info(f"Reverting AWS VM to snapshot: {snapshot_name}...")
248
+ ec2_client = boto3.client('ec2', region_name=self.region)
249
+
250
+ try:
251
+ # Get original instance details for config.
252
+ instance_details = ec2_client.describe_instances(InstanceIds=[path_to_vm])
253
+ instance = instance_details['Reservations'][0]['Instances'][0]
254
+ security_groups = [sg['GroupId'] for sg in instance['SecurityGroups']]
255
+ subnet_id = instance['SubnetId']
256
+ instance_type = instance['InstanceType']
257
+
258
+ # Terminate the old instance. This is a non-blocking call.
259
+ logger.info(f"Initiating termination for old instance {path_to_vm}...")
260
+ ec2_client.terminate_instances(InstanceIds=[path_to_vm])
261
+ logger.info(f"Old instance {path_to_vm} termination initiated.")
262
+
263
+ # Rotate to a new proxy
264
+ self._rotate_proxy()
265
+
266
+ # Create a new instance
267
+ new_instance_id = self.create_instance_with_proxy(
268
+ snapshot_name, instance_type, security_groups, subnet_id
269
+ )
270
+
271
+ # Note: VNC address is displayed within create_instance_with_proxy
272
+ logger.info(f"Successfully launched new instance {new_instance_id} for revert.")
273
+
274
+ return new_instance_id
275
+
276
+ except ClientError as e:
277
+ logger.error(f"Failed to revert to snapshot {snapshot_name} for the instance {path_to_vm}: {str(e)}")
278
+ raise
279
+
280
+ def stop_emulator(self, path_to_vm, region=None):
281
+ logger.info(f"Stopping AWS VM {path_to_vm}...")
282
+ ec2_client = boto3.client('ec2', region_name=self.region)
283
+
284
+ try:
285
+ ec2_client.stop_instances(InstanceIds=[path_to_vm])
286
+ waiter = ec2_client.get_waiter('instance_stopped')
287
+ waiter.wait(InstanceIds=[path_to_vm], WaiterConfig={'Delay': WAIT_DELAY, 'MaxAttempts': MAX_ATTEMPTS})
288
+ logger.info(f"Instance {path_to_vm} has been stopped.")
289
+ except ClientError as e:
290
+ logger.error(f"Failed to stop the AWS VM {path_to_vm}: {str(e)}")
291
+ raise
292
+
293
+ def get_current_proxy_info(self) -> Optional[dict]:
294
+ """่Žทๅ–ๅฝ“ๅ‰ไปฃ็†ไฟกๆฏ"""
295
+ if self.current_proxy:
296
+ return {
297
+ 'host': self.current_proxy.host,
298
+ 'port': self.current_proxy.port,
299
+ 'protocol': self.current_proxy.protocol,
300
+ 'failed_count': self.current_proxy.failed_count
301
+ }
302
+ return None
303
+
304
+ def force_rotate_proxy(self):
305
+ """ๅผบๅˆถ่ฝฎๆขไปฃ็†"""
306
+ logger.info("Force rotating proxy...")
307
+ if self.current_proxy:
308
+ proxy_pool = get_global_proxy_pool()
309
+ proxy_pool.mark_proxy_failed(self.current_proxy)
310
+ self._rotate_proxy()
311
+
312
+ def get_proxy_stats(self) -> dict:
313
+ """่Žทๅ–ไปฃ็†ๆฑ ็ปŸ่ฎกไฟกๆฏ"""
314
+ proxy_pool = get_global_proxy_pool()
315
+ return proxy_pool.get_stats()