gitarsenal-cli 1.8.3 โ†’ 1.8.5

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.
@@ -16,6 +16,13 @@ import signal
16
16
  from pathlib import Path
17
17
  import modal
18
18
 
19
+ # Import authentication manager
20
+ try:
21
+ from auth_manager import AuthManager
22
+ except ImportError:
23
+ print("โŒ Authentication module not found. Please ensure auth_manager.py is in the same directory.")
24
+ sys.exit(1)
25
+
19
26
  # Parse command-line arguments
20
27
  parser = argparse.ArgumentParser()
21
28
  parser.add_argument('--proxy-url', help='URL of the proxy server')
@@ -1616,7 +1623,6 @@ Consider the current directory, system information, directory contents, and avai
1616
1623
  IMPORTANT GUIDELINES:
1617
1624
  1. For any commands that might ask for yes/no confirmation, use the appropriate non-interactive flag:
1618
1625
  - For apt/apt-get: use -y or --yes
1619
- - For pip: use --no-input
1620
1626
  - For rm: use -f or --force
1621
1627
 
1622
1628
  2. If the error indicates a file is not found:
@@ -3326,6 +3332,18 @@ def show_usage_examples():
3326
3332
  """Display usage examples for the script."""
3327
3333
  print("Usage Examples\n")
3328
3334
 
3335
+ print("๐Ÿ” Authentication Commands")
3336
+ print("โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”")
3337
+ print("โ”‚ gitarsenal --register # Register new account โ”‚")
3338
+ print("โ”‚ gitarsenal --login # Login to existing account โ”‚")
3339
+ print("โ”‚ gitarsenal --logout # Logout from account โ”‚")
3340
+ print("โ”‚ gitarsenal --user-info # Show current user information โ”‚")
3341
+ print("โ”‚ gitarsenal --change-password # Change password โ”‚")
3342
+ print("โ”‚ gitarsenal --delete-account # Delete account โ”‚")
3343
+ print("โ”‚ gitarsenal --store-api-key openai # Store OpenAI API key โ”‚")
3344
+ print("โ”‚ gitarsenal --auth # Interactive auth management โ”‚")
3345
+ print("โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜\n")
3346
+
3329
3347
  print("Basic Container Creation")
3330
3348
  print("โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”")
3331
3349
  print("โ”‚ gitarsenal --gpu A10G --repo-url https://github.com/username/repo.git โ”‚")
@@ -3360,19 +3378,32 @@ def show_usage_examples():
3360
3378
  print("โ”‚ --use-api โ”‚")
3361
3379
  print("โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜\n")
3362
3380
 
3381
+ print("Development Mode (Skip Authentication)")
3382
+ print("โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”")
3383
+ print("โ”‚ gitarsenal --skip-auth --gpu A10G --repo-url https://github.com/username/repo.git โ”‚")
3384
+ print("โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜\n")
3385
+
3363
3386
  print("Available GPU Options:")
3364
3387
  print(" T4, L4, A10G, A100-40GB, A100-80GB, L40S, H100, H200, B200")
3365
3388
  print()
3389
+ print("Authentication Behavior:")
3390
+ print(" โ€ข First time: Interactive registration/login required")
3391
+ print(" โ€ข Subsequent runs: Automatic login with stored session")
3392
+ print(" โ€ข Use --skip-auth for development (bypasses auth)")
3393
+ print()
3366
3394
  print("GPU Selection Behavior:")
3367
3395
  print(" โ€ข With --gpu: Uses specified GPU without prompting")
3368
3396
  print(" โ€ข Without --gpu: Shows interactive GPU selection menu")
3369
3397
  print()
3370
3398
  print("Examples:")
3371
- print(" # Uses A10G without prompting:")
3399
+ print(" # First time setup (will prompt for registration):")
3372
3400
  print(" gitarsenal --gpu A10G --repo-url https://github.com/username/repo.git")
3373
3401
  print()
3374
- print(" # Shows interactive GPU selection menu:")
3402
+ print(" # Subsequent runs (automatic login):")
3375
3403
  print(" gitarsenal --repo-url https://github.com/username/repo.git")
3404
+ print()
3405
+ print(" # Development mode (skip authentication):")
3406
+ print(" gitarsenal --skip-auth --repo-url https://github.com/username/repo.git")
3376
3407
 
3377
3408
  def make_api_request_with_retry(url, payload, max_retries=2, timeout=180):
3378
3409
  """Make an API request with retry mechanism."""
@@ -3860,6 +3891,304 @@ def prompt_for_gpu():
3860
3891
  print("โœ… Using default GPU: A10G")
3861
3892
  return "A10G"
3862
3893
 
3894
+
3895
+
3896
+ def preprocess_commands_with_llm(setup_commands, stored_credentials, api_key=None):
3897
+ """
3898
+ Use LLM to preprocess setup commands and inject available credentials.
3899
+
3900
+ Args:
3901
+ setup_commands: List of setup commands
3902
+ stored_credentials: Dictionary of stored credentials
3903
+ api_key: OpenAI API key for LLM calls
3904
+
3905
+ Returns:
3906
+ List of processed commands with credentials injected
3907
+ """
3908
+ if not setup_commands or not stored_credentials:
3909
+ return setup_commands
3910
+
3911
+ try:
3912
+ # Create context for the LLM
3913
+ credentials_info = "\n".join([f"- {key}: {value[:8]}..." for key, value in stored_credentials.items()])
3914
+
3915
+ prompt = f"""
3916
+ You are a command preprocessing assistant. Your task is to modify setup commands to use available credentials and make them non-interactive.
3917
+
3918
+ AVAILABLE CREDENTIALS:
3919
+ {credentials_info}
3920
+
3921
+ ORIGINAL COMMANDS:
3922
+ {chr(10).join([f"{i+1}. {cmd}" for i, cmd in enumerate(setup_commands)])}
3923
+
3924
+ INSTRUCTIONS:
3925
+ 1. Replace any authentication commands with token-based versions using available credentials
3926
+ 2. Make all commands non-interactive (add --yes, --no-input, -y flags where needed)
3927
+ 3. Use environment variables or direct token injection where appropriate
3928
+ 4. Skip commands that cannot be made non-interactive due to missing credentials
3929
+ 5. Add any necessary environment variable exports
3930
+
3931
+ Return the modified commands as a JSON array of strings. If a command should be skipped, prefix it with "# SKIPPED: ".
3932
+
3933
+ Example transformations:
3934
+ - "huggingface-cli login" โ†’ "huggingface-cli login --token $HUGGINGFACE_TOKEN"
3935
+ - "npm install" โ†’ "npm install --yes"
3936
+
3937
+ Return only the JSON array, no other text.
3938
+ """
3939
+
3940
+ if not api_key:
3941
+ print("โš ๏ธ No OpenAI API key available for command preprocessing")
3942
+ return setup_commands
3943
+
3944
+ # Call OpenAI API
3945
+ import openai
3946
+ client = openai.OpenAI(api_key=api_key)
3947
+
3948
+ response = client.chat.completions.create(
3949
+ model="gpt-3.5-turbo",
3950
+ messages=[
3951
+ {"role": "system", "content": "You are a command preprocessing assistant that modifies setup commands to use available credentials and make them non-interactive."},
3952
+ {"role": "user", "content": prompt}
3953
+ ],
3954
+ temperature=0.1,
3955
+ max_tokens=2000
3956
+ )
3957
+
3958
+ result = response.choices[0].message.content.strip()
3959
+
3960
+ # Debug: Print the raw response
3961
+ print(f"๐Ÿ” LLM Response: {result[:200]}...")
3962
+
3963
+ # Parse the JSON response
3964
+ import json
3965
+ try:
3966
+ processed_commands = json.loads(result)
3967
+ if isinstance(processed_commands, list):
3968
+ print(f"๐Ÿ”ง LLM preprocessed {len(processed_commands)} commands")
3969
+ for i, cmd in enumerate(processed_commands):
3970
+ if cmd != setup_commands[i]:
3971
+ print(f" {i+1}. {setup_commands[i]} โ†’ {cmd}")
3972
+ return processed_commands
3973
+ else:
3974
+ print("โš ๏ธ LLM returned invalid format, using fallback preprocessing")
3975
+ return fallback_preprocess_commands(setup_commands, stored_credentials)
3976
+ except json.JSONDecodeError as e:
3977
+ print(f"โš ๏ธ Failed to parse LLM response: {e}")
3978
+ print("๐Ÿ”„ Using fallback preprocessing...")
3979
+ return fallback_preprocess_commands(setup_commands, stored_credentials)
3980
+
3981
+ except Exception as e:
3982
+ print(f"โš ๏ธ LLM preprocessing failed: {e}")
3983
+ print("๐Ÿ”„ Using fallback preprocessing...")
3984
+ return fallback_preprocess_commands(setup_commands, stored_credentials)
3985
+
3986
+ def fallback_preprocess_commands(setup_commands, stored_credentials):
3987
+ """
3988
+ Fallback preprocessing function that manually handles common credential injection patterns.
3989
+
3990
+ Args:
3991
+ setup_commands: List of setup commands
3992
+ stored_credentials: Dictionary of stored credentials
3993
+
3994
+ Returns:
3995
+ List of processed commands with credentials injected
3996
+ """
3997
+ if not setup_commands or not stored_credentials:
3998
+ return setup_commands
3999
+
4000
+ processed_commands = []
4001
+
4002
+ for i, command in enumerate(setup_commands):
4003
+ processed_command = command
4004
+
4005
+ # Handle Hugging Face login
4006
+ if 'huggingface-cli login' in command and '--token' not in command:
4007
+ if 'HUGGINGFACE_TOKEN' in stored_credentials:
4008
+ processed_command = f"huggingface-cli login --token $HUGGINGFACE_TOKEN"
4009
+ print(f"๐Ÿ”ง Fallback: Injected HF token into command {i+1}")
4010
+ else:
4011
+ processed_command = f"# SKIPPED: {command} (no HF token available)"
4012
+ print(f"๐Ÿ”ง Fallback: Skipped command {i+1} (no HF token)")
4013
+
4014
+ # Handle OpenAI API key
4015
+ elif 'openai' in command.lower() and 'api_key' not in command.lower():
4016
+ if 'OPENAI_API_KEY' in stored_credentials:
4017
+ processed_command = f"export OPENAI_API_KEY=$OPENAI_API_KEY && {command}"
4018
+ print(f"๐Ÿ”ง Fallback: Added OpenAI API key export to command {i+1}")
4019
+
4020
+ # Handle npm install
4021
+ elif 'npm install' in command and '--yes' not in command and '--no-interactive' not in command:
4022
+ processed_command = command.replace('npm install', 'npm install --yes')
4023
+ print(f"๐Ÿ”ง Fallback: Made npm install non-interactive in command {i+1}")
4024
+
4025
+ # Handle git clone
4026
+ elif command.strip().startswith('git clone') and '--depth 1' not in command:
4027
+ processed_command = command.replace('git clone', 'git clone --depth 1')
4028
+ print(f"๐Ÿ”ง Fallback: Made git clone non-interactive in command {i+1}")
4029
+
4030
+ # Handle apt-get install
4031
+ elif 'apt-get install' in command and '-y' not in command:
4032
+ processed_command = command.replace('apt-get install', 'apt-get install -y')
4033
+ print(f"๐Ÿ”ง Fallback: Made apt-get install non-interactive in command {i+1}")
4034
+
4035
+ processed_commands.append(processed_command)
4036
+
4037
+ print(f"๐Ÿ”ง Fallback preprocessing completed: {len(processed_commands)} commands")
4038
+ return processed_commands
4039
+
4040
+ def _check_authentication(auth_manager):
4041
+ """Check if user is authenticated, prompt for login if not"""
4042
+ if auth_manager.is_authenticated():
4043
+ user = auth_manager.get_current_user()
4044
+ print(f"โœ… Authenticated as: {user['username']}")
4045
+ return True
4046
+
4047
+ print("\n๐Ÿ” Authentication required")
4048
+ return auth_manager.interactive_auth_flow()
4049
+
4050
+ def _handle_auth_commands(auth_manager, args):
4051
+ """Handle authentication-related commands"""
4052
+ if args.login:
4053
+ print("\n๐Ÿ” LOGIN")
4054
+ username = input("Username: ").strip()
4055
+ password = getpass.getpass("Password: ").strip()
4056
+ if auth_manager.login_user(username, password):
4057
+ print("โœ… Login successful!")
4058
+ else:
4059
+ print("โŒ Login failed.")
4060
+
4061
+ elif args.register:
4062
+ print("\n๐Ÿ” REGISTRATION")
4063
+ username = input("Username (min 3 characters): ").strip()
4064
+ email = input("Email: ").strip()
4065
+ password = getpass.getpass("Password (min 8 characters): ").strip()
4066
+ confirm_password = getpass.getpass("Confirm password: ").strip()
4067
+
4068
+ if password != confirm_password:
4069
+ print("โŒ Passwords do not match.")
4070
+ return
4071
+
4072
+ if auth_manager.register_user(username, email, password):
4073
+ print("โœ… Registration successful!")
4074
+ # Auto-login after registration
4075
+ if auth_manager.login_user(username, password):
4076
+ print("โœ… Auto-login successful!")
4077
+ else:
4078
+ print("โŒ Registration failed.")
4079
+
4080
+ elif args.logout:
4081
+ auth_manager.logout_user()
4082
+
4083
+ elif args.user_info:
4084
+ auth_manager.show_user_info()
4085
+
4086
+ elif args.change_password:
4087
+ if not auth_manager.is_authenticated():
4088
+ print("โŒ Not logged in. Please login first.")
4089
+ return
4090
+
4091
+ current_password = getpass.getpass("Current password: ").strip()
4092
+ new_password = getpass.getpass("New password (min 8 characters): ").strip()
4093
+ confirm_password = getpass.getpass("Confirm new password: ").strip()
4094
+
4095
+ if new_password != confirm_password:
4096
+ print("โŒ New passwords do not match.")
4097
+ return
4098
+
4099
+ if auth_manager.change_password(current_password, new_password):
4100
+ print("โœ… Password changed successfully!")
4101
+ else:
4102
+ print("โŒ Failed to change password.")
4103
+
4104
+ elif args.delete_account:
4105
+ if not auth_manager.is_authenticated():
4106
+ print("โŒ Not logged in. Please login first.")
4107
+ return
4108
+
4109
+ password = getpass.getpass("Enter your password to confirm deletion: ").strip()
4110
+ if auth_manager.delete_account(password):
4111
+ print("โœ… Account deleted successfully!")
4112
+ else:
4113
+ print("โŒ Failed to delete account.")
4114
+
4115
+ elif args.store_api_key:
4116
+ if not auth_manager.is_authenticated():
4117
+ print("โŒ Not logged in. Please login first.")
4118
+ return
4119
+
4120
+ service = args.store_api_key
4121
+ api_key = getpass.getpass(f"Enter {service} API key: ").strip()
4122
+
4123
+ if auth_manager.store_api_key(service, api_key):
4124
+ print(f"โœ… {service} API key stored successfully!")
4125
+ else:
4126
+ print(f"โŒ Failed to store {service} API key.")
4127
+
4128
+ elif args.auth:
4129
+ # Interactive authentication management
4130
+ while True:
4131
+ print("\n" + "="*60)
4132
+ print("๐Ÿ” AUTHENTICATION MANAGEMENT")
4133
+ print("="*60)
4134
+ print("1. Login")
4135
+ print("2. Register")
4136
+ print("3. Show user info")
4137
+ print("4. Change password")
4138
+ print("5. Store API key")
4139
+ print("6. Delete account")
4140
+ print("7. Logout")
4141
+ print("8. Exit")
4142
+
4143
+ choice = input("\nSelect an option (1-8): ").strip()
4144
+
4145
+ if choice == "1":
4146
+ username = input("Username: ").strip()
4147
+ password = getpass.getpass("Password: ").strip()
4148
+ auth_manager.login_user(username, password)
4149
+ elif choice == "2":
4150
+ username = input("Username (min 3 characters): ").strip()
4151
+ email = input("Email: ").strip()
4152
+ password = getpass.getpass("Password (min 8 characters): ").strip()
4153
+ confirm_password = getpass.getpass("Confirm password: ").strip()
4154
+ if password == confirm_password:
4155
+ auth_manager.register_user(username, email, password)
4156
+ else:
4157
+ print("โŒ Passwords do not match.")
4158
+ elif choice == "3":
4159
+ auth_manager.show_user_info()
4160
+ elif choice == "4":
4161
+ if auth_manager.is_authenticated():
4162
+ current_password = getpass.getpass("Current password: ").strip()
4163
+ new_password = getpass.getpass("New password (min 8 characters): ").strip()
4164
+ confirm_password = getpass.getpass("Confirm new password: ").strip()
4165
+ if new_password == confirm_password:
4166
+ auth_manager.change_password(current_password, new_password)
4167
+ else:
4168
+ print("โŒ New passwords do not match.")
4169
+ else:
4170
+ print("โŒ Not logged in.")
4171
+ elif choice == "5":
4172
+ if auth_manager.is_authenticated():
4173
+ service = input("Service name (e.g., openai, modal): ").strip()
4174
+ api_key = getpass.getpass(f"Enter {service} API key: ").strip()
4175
+ auth_manager.store_api_key(service, api_key)
4176
+ else:
4177
+ print("โŒ Not logged in.")
4178
+ elif choice == "6":
4179
+ if auth_manager.is_authenticated():
4180
+ password = getpass.getpass("Enter your password to confirm deletion: ").strip()
4181
+ auth_manager.delete_account(password)
4182
+ else:
4183
+ print("โŒ Not logged in.")
4184
+ elif choice == "7":
4185
+ auth_manager.logout_user()
4186
+ elif choice == "8":
4187
+ print("๐Ÿ‘‹ Goodbye!")
4188
+ break
4189
+ else:
4190
+ print("โŒ Invalid option. Please try again.")
4191
+
3863
4192
  # Replace the existing GPU argument parsing in the main section
3864
4193
  if __name__ == "__main__":
3865
4194
  # Parse command line arguments when script is run directly
@@ -3885,8 +4214,27 @@ if __name__ == "__main__":
3885
4214
  parser.add_argument('--list-gpus', action='store_true', help='List available GPU types with their specifications')
3886
4215
  parser.add_argument('--interactive', action='store_true', help='Run in interactive mode with prompts')
3887
4216
 
4217
+ # Authentication-related arguments
4218
+ parser.add_argument('--auth', action='store_true', help='Manage authentication (login, register, logout)')
4219
+ parser.add_argument('--login', action='store_true', help='Login to GitArsenal')
4220
+ parser.add_argument('--register', action='store_true', help='Register new account')
4221
+ parser.add_argument('--logout', action='store_true', help='Logout from GitArsenal')
4222
+ parser.add_argument('--user-info', action='store_true', help='Show current user information')
4223
+ parser.add_argument('--change-password', action='store_true', help='Change password')
4224
+ parser.add_argument('--delete-account', action='store_true', help='Delete account')
4225
+ parser.add_argument('--store-api-key', type=str, help='Store API key for a service (e.g., openai, modal)')
4226
+ parser.add_argument('--skip-auth', action='store_true', help='Skip authentication check (for development)')
4227
+
3888
4228
  args = parser.parse_args()
3889
4229
 
4230
+ # Initialize authentication manager
4231
+ auth_manager = AuthManager()
4232
+
4233
+ # Handle authentication-related commands
4234
+ if args.auth or args.login or args.register or args.logout or args.user_info or args.change_password or args.delete_account or args.store_api_key:
4235
+ _handle_auth_commands(auth_manager, args)
4236
+ sys.exit(0)
4237
+
3890
4238
  # If --list-gpus is specified, just show GPU options and exit
3891
4239
  if args.list_gpus:
3892
4240
  prompt_for_gpu()
@@ -3897,6 +4245,13 @@ if __name__ == "__main__":
3897
4245
  show_usage_examples()
3898
4246
  sys.exit(0)
3899
4247
 
4248
+ # Check authentication (unless skipped for development)
4249
+ if not args.skip_auth:
4250
+ if not _check_authentication(auth_manager):
4251
+ print("\nโŒ Authentication required. Please login or register first.")
4252
+ print("Use --login to login or --register to create an account.")
4253
+ sys.exit(1)
4254
+
3900
4255
  # Check for dependencies
3901
4256
  print("โ  Checking dependencies...")
3902
4257
  print("--- Dependency Check ---")
@@ -4145,89 +4500,3 @@ if __name__ == "__main__":
4145
4500
  # print("๐Ÿงน Cleaning up resources...")
4146
4501
  cleanup_modal_token()
4147
4502
  sys.exit(1)
4148
-
4149
- def preprocess_commands_with_llm(setup_commands, stored_credentials, api_key=None):
4150
- """
4151
- Use LLM to preprocess setup commands and inject available credentials.
4152
-
4153
- Args:
4154
- setup_commands: List of setup commands
4155
- stored_credentials: Dictionary of stored credentials
4156
- api_key: OpenAI API key for LLM calls
4157
-
4158
- Returns:
4159
- List of processed commands with credentials injected
4160
- """
4161
- if not setup_commands or not stored_credentials:
4162
- return setup_commands
4163
-
4164
- try:
4165
- # Create context for the LLM
4166
- credentials_info = "\n".join([f"- {key}: {value[:8]}..." for key, value in stored_credentials.items()])
4167
-
4168
- prompt = f"""
4169
- You are a command preprocessing assistant. Your task is to modify setup commands to use available credentials and make them non-interactive.
4170
-
4171
- AVAILABLE CREDENTIALS:
4172
- {credentials_info}
4173
-
4174
- ORIGINAL COMMANDS:
4175
- {chr(10).join([f"{i+1}. {cmd}" for i, cmd in enumerate(setup_commands)])}
4176
-
4177
- INSTRUCTIONS:
4178
- 1. Replace any authentication commands with token-based versions using available credentials
4179
- 2. Make all commands non-interactive (add --yes, --no-input, -y flags where needed)
4180
- 3. Use environment variables or direct token injection where appropriate
4181
- 4. Skip commands that cannot be made non-interactive due to missing credentials
4182
- 5. Add any necessary environment variable exports
4183
-
4184
- Return the modified commands as a JSON array of strings. If a command should be skipped, prefix it with "# SKIPPED: ".
4185
-
4186
- Example transformations:
4187
- - "huggingface-cli login" โ†’ "huggingface-cli login --token $HUGGINGFACE_TOKEN"
4188
- - "npm install" โ†’ "npm install --yes"
4189
- - "pip install package" โ†’ "pip install package --no-input"
4190
-
4191
- Return only the JSON array, no other text.
4192
- """
4193
-
4194
- if not api_key:
4195
- print("โš ๏ธ No OpenAI API key available for command preprocessing")
4196
- return setup_commands
4197
-
4198
- # Call OpenAI API
4199
- import openai
4200
- client = openai.OpenAI(api_key=api_key)
4201
-
4202
- response = client.chat.completions.create(
4203
- model="gpt-3.5-turbo",
4204
- messages=[
4205
- {"role": "system", "content": "You are a command preprocessing assistant that modifies setup commands to use available credentials and make them non-interactive."},
4206
- {"role": "user", "content": prompt}
4207
- ],
4208
- temperature=0.1,
4209
- max_tokens=2000
4210
- )
4211
-
4212
- result = response.choices[0].message.content.strip()
4213
-
4214
- # Parse the JSON response
4215
- import json
4216
- try:
4217
- processed_commands = json.loads(result)
4218
- if isinstance(processed_commands, list):
4219
- print(f"๐Ÿ”ง LLM preprocessed {len(processed_commands)} commands")
4220
- for i, cmd in enumerate(processed_commands):
4221
- if cmd != setup_commands[i]:
4222
- print(f" {i+1}. {setup_commands[i]} โ†’ {cmd}")
4223
- return processed_commands
4224
- else:
4225
- print("โš ๏ธ LLM returned invalid format, using original commands")
4226
- return setup_commands
4227
- except json.JSONDecodeError:
4228
- print("โš ๏ธ Failed to parse LLM response, using original commands")
4229
- return setup_commands
4230
-
4231
- except Exception as e:
4232
- print(f"โš ๏ธ LLM preprocessing failed: {e}")
4233
- return setup_commands