cloudx-proxy 0.1.1__py3-none-any.whl → 0.3.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.
cloudx_proxy/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.1.1'
16
- __version_tuple__ = version_tuple = (0, 1, 1)
15
+ __version__ = version = '0.3.0'
16
+ __version_tuple__ = version_tuple = (0, 3, 0)
cloudx_proxy/cli.py CHANGED
@@ -16,9 +16,9 @@ def cli():
16
16
  @click.argument('port', type=int, default=22)
17
17
  @click.option('--profile', default='vscode', help='AWS profile to use (default: vscode)')
18
18
  @click.option('--region', help='AWS region (default: from profile, or eu-west-1 if not set)')
19
- @click.option('--key-path', help='Path to SSH public key (default: ~/.ssh/vscode/vscode.pub)')
19
+ @click.option('--ssh-key', default='vscode', help='SSH key name to use (default: vscode)')
20
20
  @click.option('--aws-env', help='AWS environment directory (default: ~/.aws, use name of directory in ~/.aws/aws-envs/)')
21
- def connect(instance_id: str, port: int, profile: str, region: str, key_path: str, aws_env: str):
21
+ def connect(instance_id: str, port: int, profile: str, region: str, ssh_key: str, aws_env: str):
22
22
  """Connect to an EC2 instance via SSM.
23
23
 
24
24
  INSTANCE_ID is the EC2 instance ID to connect to (e.g., i-0123456789abcdef0)
@@ -34,7 +34,7 @@ def connect(instance_id: str, port: int, profile: str, region: str, key_path: st
34
34
  port=port,
35
35
  profile=profile,
36
36
  region=region,
37
- public_key_path=key_path,
37
+ ssh_key=ssh_key,
38
38
  aws_env=aws_env
39
39
  )
40
40
 
@@ -65,6 +65,8 @@ def setup(profile: str, ssh_key: str, aws_env: str):
65
65
  try:
66
66
  setup = CloudXSetup(profile=profile, ssh_key=ssh_key, aws_env=aws_env)
67
67
 
68
+ print("\n\033[1;95m=== cloudx-proxy Setup ===\033[0m\n")
69
+
68
70
  # Set up AWS profile
69
71
  if not setup.setup_aws_profile():
70
72
  sys.exit(1)
@@ -73,10 +75,10 @@ def setup(profile: str, ssh_key: str, aws_env: str):
73
75
  if not setup.setup_ssh_key():
74
76
  sys.exit(1)
75
77
 
76
- # Get CloudX environment and instance details
77
- cloudx_env = click.prompt("Enter CloudX environment (e.g., dev, prod)", type=str)
78
- instance_id = click.prompt("Enter EC2 instance ID (e.g., i-0123456789abcdef0)", type=str)
79
- hostname = click.prompt("Enter hostname for the instance", type=str)
78
+ # Get environment and instance details
79
+ cloudx_env = setup.prompt("Enter environment", getattr(setup, 'default_env', None))
80
+ instance_id = setup.prompt("Enter EC2 instance ID (e.g., i-0123456789abcdef0)")
81
+ hostname = setup.prompt("Enter hostname for the instance")
80
82
 
81
83
  # Set up SSH config
82
84
  if not setup.setup_ssh_config(cloudx_env, instance_id, hostname):
@@ -86,7 +88,7 @@ def setup(profile: str, ssh_key: str, aws_env: str):
86
88
  setup.wait_for_setup_completion(instance_id)
87
89
 
88
90
  except Exception as e:
89
- print(f"Error: {str(e)}", file=sys.stderr)
91
+ print(f"\n\033[91mError: {str(e)}\033[0m", file=sys.stderr)
90
92
  sys.exit(1)
91
93
 
92
94
  if __name__ == '__main__':
cloudx_proxy/setup.py CHANGED
@@ -9,7 +9,7 @@ from botocore.exceptions import ClientError
9
9
 
10
10
  class CloudXSetup:
11
11
  def __init__(self, profile: str = "vscode", ssh_key: str = "vscode", aws_env: str = None):
12
- """Initialize CloudX setup.
12
+ """Initialize cloudx-proxy setup.
13
13
 
14
14
  Args:
15
15
  profile: AWS profile name (default: "vscode")
@@ -24,13 +24,58 @@ class CloudXSetup:
24
24
  self.ssh_config_file = self.ssh_dir / "config"
25
25
  self.ssh_key_file = self.ssh_dir / f"{ssh_key}"
26
26
  self.using_1password = False
27
+ self.default_env = None
28
+
29
+ def print_header(self, text: str) -> None:
30
+ """Print a section header.
31
+
32
+ Args:
33
+ text: The header text
34
+ """
35
+ print(f"\n\n\033[1;94m=== {text} ===\033[0m")
36
+
37
+ def print_status(self, message: str, status: bool = None, indent: int = 0) -> None:
38
+ """Print a status message with optional checkmark/cross.
39
+
40
+ Args:
41
+ message: The message to print
42
+ status: True for success (✓), False for failure (✗), None for no symbol
43
+ indent: Number of spaces to indent
44
+ """
45
+ prefix = " " * indent
46
+ if status is not None:
47
+ symbol = "✓" if status else "✗"
48
+ color = "\033[92m" if status else "\033[91m" # Green for success, red for failure
49
+ reset = "\033[0m"
50
+ print(f"{prefix}{color}{symbol}{reset} {message}")
51
+ else:
52
+ print(f"{prefix}○ {message}")
53
+
54
+ def prompt(self, message: str, default: str = None) -> str:
55
+ """Display a colored prompt for user input.
56
+
57
+ Args:
58
+ message: The prompt message
59
+ default: Default value (shown in brackets)
60
+
61
+ Returns:
62
+ str: User's input or default value
63
+ """
64
+ if default:
65
+ prompt_text = f"\033[93m{message} [{default}]: \033[0m"
66
+ else:
67
+ prompt_text = f"\033[93m{message}: \033[0m"
68
+ response = input(prompt_text)
69
+ return response if response else default
27
70
 
28
71
  def setup_aws_profile(self) -> bool:
29
72
  """Set up AWS profile using aws configure command.
30
73
 
31
74
  Returns:
32
- bool: True if profile was set up successfully
75
+ bool: True if profile was set up successfully or user chose to continue
33
76
  """
77
+ self.print_status("Checking AWS profile configuration...")
78
+
34
79
  try:
35
80
  # Configure AWS environment if specified
36
81
  if self.aws_env:
@@ -38,37 +83,45 @@ class CloudXSetup:
38
83
  os.environ["AWS_CONFIG_FILE"] = os.path.join(aws_env_dir, "config")
39
84
  os.environ["AWS_SHARED_CREDENTIALS_FILE"] = os.path.join(aws_env_dir, "credentials")
40
85
 
41
- # Check if profile exists
42
- session = boto3.Session(profile_name=self.profile)
86
+ # Try to create session with profile
43
87
  try:
44
- session.client('sts').get_caller_identity()
45
- print(f"AWS profile '{self.profile}' already exists and is valid.")
46
- return True
47
- except ClientError:
48
- pass
49
-
50
- # Profile doesn't exist or is invalid, set it up
51
- print(f"Setting up AWS profile '{self.profile}'...")
52
- print("Please enter your AWS credentials:")
53
-
54
- # Use aws configure command
55
- subprocess.run([
56
- 'aws', 'configure',
57
- '--profile', self.profile
58
- ], check=True)
88
+ session = boto3.Session(profile_name=self.profile)
89
+ except:
90
+ # Profile doesn't exist, create it
91
+ self.print_status(f"AWS profile '{self.profile}' not found", False, 2)
92
+ self.print_status("Setting up AWS profile...", None, 2)
93
+ print("\033[96mPlease enter your AWS credentials:\033[0m")
94
+
95
+ # Use aws configure command
96
+ subprocess.run([
97
+ 'aws', 'configure',
98
+ '--profile', self.profile
99
+ ], check=True)
100
+
101
+ # Create new session with configured profile
102
+ session = boto3.Session(profile_name=self.profile)
59
103
 
60
104
  # Verify the profile works
61
- session = boto3.Session(profile_name=self.profile)
62
- identity = session.client('sts').get_caller_identity()
63
- user_arn = identity['Arn']
64
-
65
- if not any(part.startswith('cloudX-') for part in user_arn.split('/')):
66
- print(f"Warning: User ARN '{user_arn}' does not match expected format cloudX-{{env}}-{{user}}")
67
-
68
- return True
105
+ try:
106
+ identity = session.client('sts').get_caller_identity()
107
+ user_arn = identity['Arn']
108
+
109
+ # Extract environment from IAM user name
110
+ user_parts = [part for part in user_arn.split('/') if part.startswith('cloudX-')]
111
+ if user_parts:
112
+ self.default_env = user_parts[0].split('-')[1] # Extract env from cloudX-{env}-{user}
113
+ self.print_status(f"AWS profile '{self.profile}' exists and matches cloudX format", True, 2)
114
+ return True
115
+ else:
116
+ self.print_status(f"AWS profile exists but doesn't match cloudX-{{env}}-{{user}} format", False, 2)
117
+ self.print_status("Please ensure your IAM user follows the format: cloudX-{env}-{username}", None, 2)
118
+ return False
119
+ except ClientError:
120
+ self.print_status("Invalid AWS credentials", False, 2)
121
+ return False
69
122
 
70
123
  except Exception as e:
71
- print(f"Error setting up AWS profile: {e}")
124
+ self.print_status(f"\033[1;91mError:\033[0m {str(e)}", False, 2)
72
125
  return False
73
126
 
74
127
  def setup_ssh_key(self) -> bool:
@@ -77,38 +130,57 @@ class CloudXSetup:
77
130
  Returns:
78
131
  bool: True if key was set up successfully
79
132
  """
133
+ self.print_header("SSH Key Configuration")
134
+ self.print_status(f"Checking SSH key '{self.ssh_key}' configuration...")
135
+
80
136
  try:
81
137
  # Create .ssh/vscode directory if it doesn't exist
82
138
  self.ssh_dir.mkdir(parents=True, exist_ok=True)
139
+ self.print_status("SSH directory exists", True, 2)
83
140
 
84
141
  key_exists = self.ssh_key_file.exists() and (self.ssh_key_file.with_suffix('.pub')).exists()
85
142
 
86
143
  if key_exists:
87
- print(f"SSH key '{self.ssh_key}' already exists.")
88
- self.using_1password = input("Would you like to use 1Password SSH agent? (y/N): ").lower() == 'y'
89
- if not self.using_1password:
90
- store_in_1password = input("Would you like to store the private key in 1Password? (y/N): ").lower() == 'y'
144
+ self.print_status(f"SSH key '{self.ssh_key}' exists", True, 2)
145
+ self.using_1password = self.prompt("Would you like to use 1Password SSH agent?", "N").lower() == 'y'
146
+ if self.using_1password:
147
+ self.print_status("Using 1Password SSH agent", True, 2)
148
+ else:
149
+ store_in_1password = self.prompt("Would you like to store the private key in 1Password?", "N").lower() == 'y'
91
150
  if store_in_1password:
92
- self._store_key_in_1password()
151
+ if self._store_key_in_1password():
152
+ self.print_status("Private key stored in 1Password", True, 2)
153
+ else:
154
+ self.print_status("Failed to store private key in 1Password", False, 2)
93
155
  else:
94
- print(f"Generating new SSH key '{self.ssh_key}'...")
156
+ self.print_status(f"Generating new SSH key '{self.ssh_key}'...", None, 2)
95
157
  subprocess.run([
96
158
  'ssh-keygen',
97
159
  '-t', 'ed25519',
98
160
  '-f', str(self.ssh_key_file),
99
161
  '-N', '' # Empty passphrase
100
162
  ], check=True)
163
+ self.print_status("SSH key generated", True, 2)
101
164
 
102
- self.using_1password = input("Would you like to use 1Password SSH agent? (y/N): ").lower() == 'y'
103
- if not self.using_1password:
104
- store_in_1password = input("Would you like to store the private key in 1Password? (y/N): ").lower() == 'y'
165
+ self.using_1password = self.prompt("Would you like to use 1Password SSH agent?", "N").lower() == 'y'
166
+ if self.using_1password:
167
+ self.print_status("Using 1Password SSH agent", True, 2)
168
+ else:
169
+ store_in_1password = self.prompt("Would you like to store the private key in 1Password?", "N").lower() == 'y'
105
170
  if store_in_1password:
106
- self._store_key_in_1password()
171
+ if self._store_key_in_1password():
172
+ self.print_status("Private key stored in 1Password", True, 2)
173
+ else:
174
+ self.print_status("Failed to store private key in 1Password", False, 2)
107
175
 
108
176
  return True
109
177
 
110
178
  except Exception as e:
111
- print(f"Error setting up SSH key: {e}")
179
+ self.print_status(f"Error: {str(e)}", False, 2)
180
+ continue_setup = self.prompt("Would you like to continue anyway?", "Y").lower() != 'n'
181
+ if continue_setup:
182
+ self.print_status("Continuing setup despite SSH key issues", None, 2)
183
+ return True
112
184
  return False
113
185
 
114
186
  def _store_key_in_1password(self) -> bool:
@@ -123,13 +195,65 @@ class CloudXSetup:
123
195
  subprocess.run([
124
196
  'op', 'document', 'create',
125
197
  str(self.ssh_key_file),
126
- '--title', f'CloudX SSH Key - {self.ssh_key}'
198
+ '--title', f'cloudx-proxy SSH Key - {self.ssh_key}'
127
199
  ], check=True)
128
200
  return True
129
201
  except subprocess.CalledProcessError:
130
202
  print("Error: 1Password CLI not installed or not signed in.")
131
203
  return False
132
204
 
205
+ def _add_host_entry(self, cloudx_env: str, instance_id: str, hostname: str, current_config: str) -> bool:
206
+ """Add settings to a specific host entry.
207
+
208
+ Args:
209
+ cloudx_env: CloudX environment
210
+ instance_id: EC2 instance ID
211
+ hostname: Hostname for the instance
212
+ current_config: Current SSH config content
213
+
214
+ Returns:
215
+ bool: True if settings were added successfully
216
+ """
217
+ try:
218
+ # Build host entry with all settings
219
+ proxy_command = "uvx cloudx-proxy connect %h %p"
220
+ if self.profile != "vscode":
221
+ proxy_command += f" --profile {self.profile}"
222
+ if self.aws_env:
223
+ proxy_command += f" --aws-env {self.aws_env}"
224
+ if self.ssh_key != "vscode":
225
+ proxy_command += f" --ssh-key {self.ssh_key}"
226
+
227
+ host_entry = f"""
228
+ Host cloudx-{cloudx_env}-{hostname}
229
+ HostName {instance_id}
230
+ User ec2-user
231
+ """
232
+ if self.using_1password:
233
+ host_entry += f""" IdentityAgent ~/.1password/agent.sock
234
+ IdentityFile {self.ssh_key_file}.pub
235
+ IdentitiesOnly yes
236
+ """
237
+ else:
238
+ host_entry += f""" IdentityFile {self.ssh_key_file}
239
+ """
240
+ host_entry += f""" ProxyCommand {proxy_command}
241
+ """
242
+
243
+ # Append host entry
244
+ with open(self.ssh_config_file, 'a') as f:
245
+ f.write(host_entry)
246
+ self.print_status("Host entry added with settings", True, 2)
247
+ return True
248
+
249
+ except Exception as e:
250
+ self.print_status(f"\033[1;91mError:\033[0m {str(e)}", False, 2)
251
+ continue_setup = self.prompt("Would you like to continue anyway?", "Y").lower() != 'n'
252
+ if continue_setup:
253
+ self.print_status("Continuing setup despite SSH config issues", None, 2)
254
+ return True
255
+ return False
256
+
133
257
  def setup_ssh_config(self, cloudx_env: str, instance_id: str, hostname: str) -> bool:
134
258
  """Set up SSH config for the instance.
135
259
 
@@ -167,55 +291,88 @@ class CloudXSetup:
167
291
  Returns:
168
292
  bool: True if config was set up successfully
169
293
  """
294
+ self.print_header("SSH Configuration")
295
+ self.print_status("Setting up SSH configuration...")
296
+
170
297
  try:
171
- # Check if we need to create base config
172
- need_base_config = True
298
+ # Check existing configuration
173
299
  if self.ssh_config_file.exists():
174
300
  current_config = self.ssh_config_file.read_text()
175
301
  # Check if configuration for this environment already exists
176
302
  if f"Host cloudx-{cloudx_env}-*" in current_config:
177
- need_base_config = False
178
-
179
- if need_base_config:
180
- # Build ProxyCommand with all necessary parameters
181
- proxy_command = f"uvx cloudx-proxy connect %h %p --profile {self.profile}"
182
- if self.aws_env:
183
- proxy_command += f" --aws-env {self.aws_env}"
184
- if self.ssh_key != "vscode":
185
- proxy_command += f" --key-path {self.ssh_key_file}.pub"
303
+ self.print_status(f"Found existing config for cloudx-{cloudx_env}-*", True, 2)
304
+ choice = self.prompt(
305
+ "Would you like to (1) override the existing config or "
306
+ "(2) add settings to the specific host entry?",
307
+ "1"
308
+ )
309
+ if choice == "2":
310
+ # Add settings to specific host entry
311
+ self.print_status("Adding settings to specific host entry", None, 2)
312
+ return self._add_host_entry(cloudx_env, instance_id, hostname, current_config)
313
+ else:
314
+ # Remove existing config for this environment
315
+ self.print_status("Removing existing configuration", None, 2)
316
+ lines = current_config.splitlines()
317
+ new_lines = []
318
+ skip = False
319
+ for line in lines:
320
+ if line.strip() == f"Host cloudx-{cloudx_env}-*":
321
+ skip = True
322
+ elif skip and line.startswith("Host "):
323
+ skip = False
324
+ if not skip:
325
+ new_lines.append(line)
326
+ current_config = "\n".join(new_lines)
327
+ with open(self.ssh_config_file, 'w') as f:
328
+ f.write(current_config)
329
+
330
+ # Create base config
331
+ self.print_status(f"Creating new config for cloudx-{cloudx_env}-*", None, 2)
332
+ # Build ProxyCommand with only non-default parameters
333
+ proxy_command = "uvx cloudx-proxy connect %h %p"
334
+ if self.profile != "vscode":
335
+ proxy_command += f" --profile {self.profile}"
336
+ if self.aws_env:
337
+ proxy_command += f" --aws-env {self.aws_env}"
338
+ if self.ssh_key != "vscode":
339
+ proxy_command += f" --ssh-key {self.ssh_key}"
186
340
 
187
- # Build base configuration
188
- base_config = f"""# CloudX SSH Configuration
341
+ # Build base configuration
342
+ base_config = f"""# cloudx-proxy SSH Configuration
189
343
  Host cloudx-{cloudx_env}-*
190
344
  User ec2-user
191
345
  """
192
- # Add 1Password or standard key configuration
193
- if self.using_1password:
194
- base_config += f""" IdentityAgent ~/.1password/agent.sock
346
+ # Add 1Password or standard key configuration
347
+ if self.using_1password:
348
+ base_config += f""" IdentityAgent ~/.1password/agent.sock
195
349
  IdentityFile {self.ssh_key_file}.pub
196
350
  IdentitiesOnly yes
197
351
  """
198
- else:
199
- base_config += f""" IdentityFile {self.ssh_key_file}
352
+ else:
353
+ base_config += f""" IdentityFile {self.ssh_key_file}
200
354
  """
201
- # Add ProxyCommand
202
- base_config += f""" ProxyCommand {proxy_command}
355
+ # Add ProxyCommand
356
+ base_config += f""" ProxyCommand {proxy_command}
203
357
  """
204
-
205
- # If file exists, append the new config, otherwise create it
206
- if self.ssh_config_file.exists():
207
- with open(self.ssh_config_file, 'a') as f:
208
- f.write("\n" + base_config)
209
- else:
210
- self.ssh_config_file.write_text(base_config)
358
+
359
+ # If file exists, append the new config, otherwise create it
360
+ if self.ssh_config_file.exists():
361
+ with open(self.ssh_config_file, 'a') as f:
362
+ f.write("\n" + base_config)
363
+ else:
364
+ self.ssh_config_file.write_text(base_config)
365
+ self.print_status("Base configuration created", True, 2)
211
366
 
212
367
  # Add specific host entry
368
+ self.print_status(f"Adding host entry for cloudx-{cloudx_env}-{hostname}", None, 2)
213
369
  host_entry = f"""
214
370
  Host cloudx-{cloudx_env}-{hostname}
215
371
  HostName {instance_id}
216
372
  """
217
373
  with open(self.ssh_config_file, 'a') as f:
218
374
  f.write(host_entry)
375
+ self.print_status("Host entry added", True, 2)
219
376
 
220
377
  # Ensure main SSH config includes our config
221
378
  main_config = Path(self.home_dir) / ".ssh" / "config"
@@ -226,18 +383,26 @@ Host cloudx-{cloudx_env}-{hostname}
226
383
  if include_line not in content:
227
384
  with open(main_config, 'a') as f:
228
385
  f.write(f"\n{include_line}")
386
+ self.print_status("Added include line to main SSH config", True, 2)
387
+ else:
388
+ self.print_status("Main SSH config already includes our config", True, 2)
229
389
  else:
230
390
  main_config.write_text(include_line)
391
+ self.print_status("Created main SSH config with include line", True, 2)
231
392
 
232
- print(f"\nSSH configuration has been set up:")
233
- print(f"- Main config file: {main_config}")
234
- print(f"- CloudX config file: {self.ssh_config_file}")
235
- print(f"\nYou can now connect using: ssh cloudx-{cloudx_env}-{hostname}")
393
+ self.print_status("\nSSH configuration summary:", None)
394
+ self.print_status(f"Main config: {main_config}", None, 2)
395
+ self.print_status(f"cloudx-proxy config: {self.ssh_config_file}", None, 2)
396
+ self.print_status(f"Connect using: ssh cloudx-{cloudx_env}-{hostname}", None, 2)
236
397
 
237
398
  return True
238
399
 
239
400
  except Exception as e:
240
- print(f"Error setting up SSH config: {e}")
401
+ self.print_status(f"\033[1;91mError:\033[0m {str(e)}", False, 2)
402
+ continue_setup = self.prompt("Would you like to continue anyway?", "Y").lower() != 'n'
403
+ if continue_setup:
404
+ self.print_status("Continuing setup despite SSH config issues", None, 2)
405
+ return True
241
406
  return False
242
407
 
243
408
  def check_instance_setup(self, instance_id: str) -> Tuple[bool, bool]:
@@ -254,15 +419,20 @@ Host cloudx-{cloudx_env}-{hostname}
254
419
  ssm = session.client('ssm')
255
420
 
256
421
  # Check if instance is online in SSM
422
+ self.print_status("Checking instance status in SSM...", None, 4)
257
423
  response = ssm.describe_instance_information(
258
424
  Filters=[{'Key': 'InstanceIds', 'Values': [instance_id]}]
259
425
  )
260
426
  is_running = bool(response['InstanceInformationList'])
261
427
 
262
428
  if not is_running:
429
+ self.print_status("Instance is not accessible via SSM", False, 4)
263
430
  return False, False
264
431
 
432
+ self.print_status("Instance is accessible via SSM", True, 4)
433
+
265
434
  # Check setup status using SSM command
435
+ self.print_status("Checking setup status...", None, 4)
266
436
  response = ssm.send_command(
267
437
  InstanceIds=[instance_id],
268
438
  DocumentName='AWS-RunShellScript',
@@ -289,10 +459,16 @@ Host cloudx-{cloudx_env}-{hostname}
289
459
 
290
460
  is_setup_complete = result['Status'] == 'Success' and result['StandardOutputContent'].strip() == 'DONE'
291
461
 
462
+ if is_setup_complete:
463
+ self.print_status("Setup is complete", True, 4)
464
+ else:
465
+ status = result['StandardOutputContent'].strip()
466
+ self.print_status(f"Setup status: {status}", None, 4)
467
+
292
468
  return True, is_setup_complete
293
469
 
294
470
  except Exception as e:
295
- print(f"Error checking instance setup: {e}")
471
+ self.print_status(f"\033[1;91mError:\033[0m {str(e)}", False, 4)
296
472
  return False, False
297
473
 
298
474
  def wait_for_setup_completion(self, instance_id: str) -> bool:
@@ -304,33 +480,45 @@ Host cloudx-{cloudx_env}-{hostname}
304
480
  Returns:
305
481
  bool: True if setup completed successfully
306
482
  """
307
- print(f"Checking setup status for instance {instance_id}...")
483
+ self.print_header("Instance Setup Check")
484
+ self.print_status(f"Checking instance {instance_id} setup status...")
308
485
 
309
486
  is_running, is_complete = self.check_instance_setup(instance_id)
310
487
 
311
488
  if not is_running:
312
- print("Error: Instance is not running or not accessible via SSM")
489
+ self.print_status("Instance is not running or not accessible via SSM", False, 2)
490
+ continue_setup = self.prompt("Would you like to continue anyway?", "Y").lower() != 'n'
491
+ if continue_setup:
492
+ self.print_status("Continuing setup despite instance access issues", None, 2)
493
+ return True
313
494
  return False
314
495
 
315
496
  if is_complete:
316
- print("Instance setup is already complete")
497
+ self.print_status("Instance setup is complete", True, 2)
317
498
  return True
318
499
 
319
- wait = input("Instance setup is not complete. Would you like to wait? (Y/n): ").lower() != 'n'
500
+ wait = self.prompt("Instance setup is not complete. Would you like to wait?", "Y").lower() != 'n'
320
501
  if not wait:
321
- return False
502
+ self.print_status("Skipping instance setup check", None, 2)
503
+ return True
322
504
 
323
- print("Waiting for setup to complete", end='', flush=True)
505
+ self.print_status("Waiting for setup to complete...", None, 2)
506
+ dots = 0
324
507
  while True:
325
508
  is_running, is_complete = self.check_instance_setup(instance_id)
326
509
 
327
510
  if not is_running:
328
- print("\nError: Instance is no longer running or accessible")
511
+ self.print_status("Instance is no longer running or accessible", False, 2)
512
+ continue_setup = self.prompt("Would you like to continue anyway?", "Y").lower() != 'n'
513
+ if continue_setup:
514
+ self.print_status("Continuing setup despite instance issues", None, 2)
515
+ return True
329
516
  return False
330
517
 
331
518
  if is_complete:
332
- print("\nInstance setup completed successfully")
519
+ self.print_status("Instance setup completed successfully", True, 2)
333
520
  return True
334
521
 
335
- print(".", end='', flush=True)
522
+ dots = (dots + 1) % 4
523
+ print(f"\r {'.' * dots}{' ' * (3 - dots)}", end='', flush=True)
336
524
  time.sleep(10)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cloudx-proxy
3
- Version: 0.1.1
3
+ Version: 0.3.0
4
4
  Summary: SSH proxy command to connect VSCode with Cloud9/CloudX instance using AWS Systems Manager
5
5
  Author-email: easytocloud <info@easytocloud.com>
6
6
  License: MIT License
@@ -0,0 +1,11 @@
1
+ cloudx_proxy/__init__.py,sha256=ZZ2O_m9OFJm18AxMSuYJt4UjSuSqyJlYRaZMoets498,61
2
+ cloudx_proxy/_version.py,sha256=Jk2iAU7m-7Vx9XV1TtdD9ZoJraIncDq_4_Wd-qtUotg,411
3
+ cloudx_proxy/cli.py,sha256=Ph-m8lDsdU2zZab9Y6YgBBzd_UDouBnfNrYFFx0bI_E,3426
4
+ cloudx_proxy/core.py,sha256=j6CUKdg2Ikcoi-05ceXMGA_c1aGWBhN9_JevbkLkaUY,7383
5
+ cloudx_proxy/setup.py,sha256=Y8YYMJ47fb57FAr6llQaFGuVOQ-fstYEg_Pdv5uCd-A,22486
6
+ cloudx_proxy-0.3.0.dist-info/LICENSE,sha256=i7P2OR4zsJYsMWcCUDe_B9ZfGi9bU0K5I2nKfDrW_N8,1068
7
+ cloudx_proxy-0.3.0.dist-info/METADATA,sha256=A2WZ_EGsVVB-DadxbUYDy1oeUVaSoArx3IuePpdv0DQ,10216
8
+ cloudx_proxy-0.3.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
9
+ cloudx_proxy-0.3.0.dist-info/entry_points.txt,sha256=HGt743N2lVlKd7O1qWq3C0aEHyS5PjPnxzDHh7hwtSg,54
10
+ cloudx_proxy-0.3.0.dist-info/top_level.txt,sha256=2wtEote1db21j-VvkCJFfT-dLlauuG5indjggYh3xDg,13
11
+ cloudx_proxy-0.3.0.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- cloudx_proxy/__init__.py,sha256=ZZ2O_m9OFJm18AxMSuYJt4UjSuSqyJlYRaZMoets498,61
2
- cloudx_proxy/_version.py,sha256=PKIMyjdUACH4-ONvtunQCnYE2UhlMfp9su83e3HXl5E,411
3
- cloudx_proxy/cli.py,sha256=zqOIynqNr76JE6VRF7ifn3Fr0Z5C6CjnAHQgupF30BU,3374
4
- cloudx_proxy/core.py,sha256=j6CUKdg2Ikcoi-05ceXMGA_c1aGWBhN9_JevbkLkaUY,7383
5
- cloudx_proxy/setup.py,sha256=5GGfPgIiEWwkFEY-HnnPvT_8iD94WqjoFjJ5tGSHz8o,13157
6
- cloudx_proxy-0.1.1.dist-info/LICENSE,sha256=i7P2OR4zsJYsMWcCUDe_B9ZfGi9bU0K5I2nKfDrW_N8,1068
7
- cloudx_proxy-0.1.1.dist-info/METADATA,sha256=tBwYeyqWuPOsREgWQylDRn6je6FZ1REbBQu5Pgk6I1s,10216
8
- cloudx_proxy-0.1.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
9
- cloudx_proxy-0.1.1.dist-info/entry_points.txt,sha256=HGt743N2lVlKd7O1qWq3C0aEHyS5PjPnxzDHh7hwtSg,54
10
- cloudx_proxy-0.1.1.dist-info/top_level.txt,sha256=2wtEote1db21j-VvkCJFfT-dLlauuG5indjggYh3xDg,13
11
- cloudx_proxy-0.1.1.dist-info/RECORD,,