gitarsenal-cli 1.6.11 → 1.6.13

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.
package/bin/gitarsenal.js CHANGED
@@ -60,7 +60,7 @@ const keysCmd = program
60
60
  keysCmd
61
61
  .command('add')
62
62
  .description('Add an API key')
63
- .option('-s, --service <service>', 'Service name (openai, wandb, huggingface)')
63
+ .option('-s, --service <service>', 'Service name (any service supported)')
64
64
  .option('-k, --key <key>', 'API key (if not provided, will prompt)')
65
65
  .action(async (options) => {
66
66
  await handleKeysAdd(options);
@@ -277,15 +277,10 @@ async function handleKeysAdd(options) {
277
277
  spinner.stop();
278
278
  const serviceAnswer = await inquirer.prompt([
279
279
  {
280
- type: 'list',
280
+ type: 'input',
281
281
  name: 'service',
282
- message: 'Select service:',
283
- choices: [
284
- { name: 'OpenAI', value: 'openai' },
285
- { name: 'Weights & Biases', value: 'wandb' },
286
- { name: 'Hugging Face', value: 'huggingface' },
287
- { name: 'GitArsenal OpenAI (for debugging)', value: 'gitarsenal-openai' }
288
- ]
282
+ message: 'Enter service name:',
283
+ validate: (input) => input.trim() !== '' ? true : 'Service name is required'
289
284
  }
290
285
  ]);
291
286
  service = serviceAnswer.service;
@@ -377,15 +372,10 @@ async function handleKeysView(options) {
377
372
  spinner.stop();
378
373
  const serviceAnswer = await inquirer.prompt([
379
374
  {
380
- type: 'list',
375
+ type: 'input',
381
376
  name: 'service',
382
- message: 'Select service:',
383
- choices: [
384
- { name: 'OpenAI', value: 'openai' },
385
- { name: 'Weights & Biases', value: 'wandb' },
386
- { name: 'Hugging Face', value: 'huggingface' },
387
- { name: 'GitArsenal OpenAI (for debugging)', value: 'gitarsenal-openai' }
388
- ]
377
+ message: 'Enter service name:',
378
+ validate: (input) => input.trim() !== '' ? true : 'Service name is required'
389
379
  }
390
380
  ]);
391
381
  service = serviceAnswer.service;
@@ -426,15 +416,10 @@ async function handleKeysDelete(options) {
426
416
  spinner.stop();
427
417
  const serviceAnswer = await inquirer.prompt([
428
418
  {
429
- type: 'list',
419
+ type: 'input',
430
420
  name: 'service',
431
- message: 'Select service:',
432
- choices: [
433
- { name: 'OpenAI', value: 'openai' },
434
- { name: 'Weights & Biases', value: 'wandb' },
435
- { name: 'Hugging Face', value: 'huggingface' },
436
- { name: 'GitArsenal OpenAI (for debugging)', value: 'gitarsenal-openai' }
437
- ]
421
+ message: 'Enter service name:',
422
+ validate: (input) => input.trim() !== '' ? true : 'Service name is required'
438
423
  }
439
424
  ]);
440
425
  service = serviceAnswer.service;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitarsenal-cli",
3
- "version": "1.6.11",
3
+ "version": "1.6.13",
4
4
  "description": "CLI tool for creating Modal sandboxes with GitHub repositories",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -41,23 +41,51 @@ def handle_add(credentials_manager, args):
41
41
  sys.exit(1)
42
42
 
43
43
  if not key:
44
- print("āŒ API key is required. Use --key option.")
45
- sys.exit(1)
46
-
47
- # Map service names to credential keys
48
- service_mapping = {
44
+ # Prompt for the API key interactively
45
+ print(f"\nšŸ”‘ {service.upper()} API KEY REQUIRED")
46
+ print("=" * 50)
47
+
48
+ # Get appropriate prompt based on service
49
+ prompts = {
50
+ 'openai': "Please enter your OpenAI API key:\nYou can get your API key from: https://platform.openai.com/api-keys",
51
+ 'wandb': "Please enter your Weights & Biases API key:\nYou can get your API key from: https://wandb.ai/authorize",
52
+ 'huggingface': "Please enter your Hugging Face token:\nYou can get your token from: https://huggingface.co/settings/tokens",
53
+ 'gitarsenal-openai': "Please enter GitArsenal's OpenAI API key for debugging:",
54
+ 'claude': "Please enter your Claude API key:\nYou can get your API key from: https://console.anthropic.com/"
55
+ }
56
+
57
+ prompt = prompts.get(service, f"Please enter your {service} API key:")
58
+ print(prompt)
59
+ print("-" * 50)
60
+
61
+ try:
62
+ import getpass
63
+ key = getpass.getpass("API Key (hidden): ").strip()
64
+ if not key:
65
+ print("āŒ No API key provided.")
66
+ sys.exit(1)
67
+ print("āœ… API key received successfully!")
68
+ except KeyboardInterrupt:
69
+ print("\nāŒ API key input cancelled by user.")
70
+ sys.exit(1)
71
+ except Exception as e:
72
+ print(f"āŒ Error getting API key: {e}")
73
+ sys.exit(1)
74
+
75
+ # Generate credential key from service name
76
+ # Convert service name to a standardized key format
77
+ credential_key = f"{service.replace('-', '_')}_api_key"
78
+
79
+ # Special mappings for backward compatibility
80
+ special_mappings = {
49
81
  'openai': 'openai_api_key',
50
82
  'wandb': 'wandb_api_key',
51
83
  'huggingface': 'huggingface_token',
52
84
  'gitarsenal-openai': 'gitarsenal_openai_api_key'
53
85
  }
54
86
 
55
- if service not in service_mapping:
56
- print(f"āŒ Unsupported service: {service}")
57
- print("Supported services: openai, wandb, huggingface, gitarsenal-openai")
58
- sys.exit(1)
59
-
60
- credential_key = service_mapping[service]
87
+ # Use special mapping if it exists, otherwise use generated key
88
+ credential_key = special_mappings.get(service, credential_key)
61
89
 
62
90
  # Save the credential
63
91
  credentials = credentials_manager.load_credentials()
@@ -82,19 +110,26 @@ def handle_list(credentials_manager):
82
110
  print("šŸ”‘ Stored API Keys:")
83
111
  print("=" * 50)
84
112
 
85
- # Map credential keys to display names
113
+ # Map credential keys to display names (for known services)
86
114
  key_mapping = {
87
115
  'openai_api_key': 'OpenAI',
88
116
  'wandb_api_key': 'Weights & Biases',
89
117
  'huggingface_token': 'Hugging Face',
90
- 'gitarsenal_openai_api_key': 'GitArsenal OpenAI'
118
+ 'gitarsenal_openai_api_key': 'GitArsenal OpenAI',
119
+ 'claude_api_key': 'Claude'
91
120
  }
92
121
 
93
122
  for key, value in credentials.items():
123
+ # Generate display name from key if not in mapping
94
124
  if key in key_mapping:
95
125
  display_name = key_mapping[key]
96
- masked_value = value[:8] + "*" * (len(value) - 12) + value[-4:] if len(value) > 12 else "*" * len(value)
97
- print(f" {display_name}: {masked_value}")
126
+ else:
127
+ # Convert key back to service name for display
128
+ service_name = key.replace('_api_key', '').replace('_token', '').replace('_', '-')
129
+ display_name = service_name.title()
130
+
131
+ masked_value = value[:8] + "*" * (len(value) - 12) + value[-4:] if len(value) > 12 else "*" * len(value)
132
+ print(f" {display_name}: {masked_value}")
98
133
 
99
134
  print(f"\nšŸ“ Storage location: {credentials_manager.credentials_file}")
100
135
 
@@ -106,20 +141,19 @@ def handle_view(credentials_manager, args):
106
141
  print("āŒ Service name is required. Use --service option.")
107
142
  sys.exit(1)
108
143
 
109
- # Map service names to credential keys
110
- service_mapping = {
144
+ # Generate credential key from service name
145
+ credential_key = f"{service.replace('-', '_')}_api_key"
146
+
147
+ # Special mappings for backward compatibility
148
+ special_mappings = {
111
149
  'openai': 'openai_api_key',
112
150
  'wandb': 'wandb_api_key',
113
151
  'huggingface': 'huggingface_token',
114
152
  'gitarsenal-openai': 'gitarsenal_openai_api_key'
115
153
  }
116
154
 
117
- if service not in service_mapping:
118
- print(f"āŒ Unsupported service: {service}")
119
- print("Supported services: openai, wandb, huggingface, gitarsenal-openai")
120
- sys.exit(1)
121
-
122
- credential_key = service_mapping[service]
155
+ # Use special mapping if it exists, otherwise use generated key
156
+ credential_key = special_mappings.get(service, credential_key)
123
157
  credentials = credentials_manager.load_credentials()
124
158
 
125
159
  if credential_key not in credentials:
@@ -141,20 +175,19 @@ def handle_delete(credentials_manager, args):
141
175
  print("āŒ Service name is required. Use --service option.")
142
176
  sys.exit(1)
143
177
 
144
- # Map service names to credential keys
145
- service_mapping = {
178
+ # Generate credential key from service name
179
+ credential_key = f"{service.replace('-', '_')}_api_key"
180
+
181
+ # Special mappings for backward compatibility
182
+ special_mappings = {
146
183
  'openai': 'openai_api_key',
147
184
  'wandb': 'wandb_api_key',
148
185
  'huggingface': 'huggingface_token',
149
186
  'gitarsenal-openai': 'gitarsenal_openai_api_key'
150
187
  }
151
188
 
152
- if service not in service_mapping:
153
- print(f"āŒ Unsupported service: {service}")
154
- print("Supported services: openai, wandb, huggingface, gitarsenal-openai")
155
- sys.exit(1)
156
-
157
- credential_key = service_mapping[service]
189
+ # Use special mapping if it exists, otherwise use generated key
190
+ credential_key = special_mappings.get(service, credential_key)
158
191
  success = credentials_manager.clear_credential(credential_key)
159
192
 
160
193
  if success:
@@ -418,38 +418,59 @@ def call_openai_for_debug(command, error_output, api_key=None, current_dir=None,
418
418
  except Exception as e:
419
419
  print(f"āš ļø Could not load saved API key: {e}")
420
420
 
421
- # Then try credentials manager for GitArsenal's debugging key
421
+ # Then try credentials manager
422
422
  if not api_key:
423
- print("šŸ” DEBUG: Trying credentials manager for GitArsenal's debugging key...")
423
+ print("šŸ” DEBUG: Trying credentials manager...")
424
424
  try:
425
425
  from credentials_manager import CredentialsManager
426
426
  credentials_manager = CredentialsManager()
427
- api_key = credentials_manager.get_gitarsenal_openai_api_key()
427
+ api_key = credentials_manager.get_openai_api_key()
428
428
  if api_key:
429
- print(f"šŸ” DEBUG: GitArsenal API key from credentials manager: Found")
430
- print(f"šŸ” DEBUG: GitArsenal API key value: {api_key}")
429
+ print(f"šŸ” DEBUG: API key from credentials manager: Found")
430
+ print(f"šŸ” DEBUG: Credentials manager API key value: {api_key}")
431
431
  # Set in environment for this session
432
432
  os.environ["OPENAI_API_KEY"] = api_key
433
433
  else:
434
- print(f"šŸ” DEBUG: GitArsenal API key from credentials manager: Not found")
434
+ print(f"šŸ” DEBUG: API key from credentials manager: Not found")
435
435
  except ImportError as e:
436
436
  print(f"šŸ” DEBUG: Credentials manager not available: {e}")
437
437
  # Fall back to direct input if credentials_manager is not available
438
438
  pass
439
439
 
440
- # If still no GitArsenal API key found, skip debugging
440
+ # Finally, prompt the user if still no API key
441
441
  if not api_key:
442
- print("šŸ” DEBUG: No GitArsenal API key found for debugging")
443
- print("āš ļø Skipping LLM debugging - GitArsenal's API key not available")
444
- return None
442
+ print("šŸ” DEBUG: No API key found in any source, prompting user...")
443
+ print("\n" + "="*60)
444
+ print("šŸ”‘ OPENAI API KEY REQUIRED FOR DEBUGGING")
445
+ print("="*60)
446
+ print("To debug failed commands, an OpenAI API key is needed.")
447
+ print("šŸ“ Please paste your OpenAI API key below:")
448
+ print(" (Your input will be hidden for security)")
449
+ print("-" * 60)
450
+
451
+ try:
452
+ api_key = getpass.getpass("OpenAI API Key: ").strip()
453
+ if not api_key:
454
+ print("āŒ No API key provided. Skipping debugging.")
455
+ return None
456
+ print("āœ… API key received successfully!")
457
+ print(f"šŸ” DEBUG: User-provided API key: {api_key}")
458
+ # Save the API key to environment for future use in this session
459
+ os.environ["OPENAI_API_KEY"] = api_key
460
+ except KeyboardInterrupt:
461
+ print("\nāŒ API key input cancelled by user.")
462
+ return None
463
+ except Exception as e:
464
+ print(f"āŒ Error getting API key: {e}")
465
+ return None
445
466
 
446
467
  # If we still don't have an API key, we can't proceed
447
468
  if not api_key:
448
- print("āŒ No GitArsenal OpenAI API key available. Cannot perform LLM debugging.")
449
- print("šŸ’” GitArsenal's debugging API key is not available")
469
+ print("āŒ No OpenAI API key available. Cannot perform LLM debugging.")
470
+ print("šŸ’” To enable LLM debugging, set the OPENAI_API_KEY environment variable")
450
471
  return None
451
472
 
452
- print(f"āœ… GitArsenal OpenAI API key available for debugging (length: {len(api_key)})")
473
+ print(f"āœ… OpenAI API key available (length: {len(api_key)})")
453
474
 
454
475
  # Gather additional context to help with debugging
455
476
  directory_context = ""
@@ -2338,6 +2359,29 @@ def get_setup_commands_from_gitingest(repo_url):
2338
2359
  if commands:
2339
2360
  print(f"āœ… Successfully fetched {len(commands)} setup commands from API at {api_url}")
2340
2361
 
2362
+ # Enhanced response handling for API key detection
2363
+ if "requiredApiKeys" in result:
2364
+ api_keys = result.get("requiredApiKeys", [])
2365
+ if api_keys:
2366
+ print(f"\nšŸ”‘ Required API Keys ({len(api_keys)}):")
2367
+ for i, api_key in enumerate(api_keys, 1):
2368
+ status = "šŸ”“ Required" if api_key.get("required", False) else "🟔 Optional"
2369
+ print(f" {i}. {api_key.get('name', 'Unknown')} - {status}")
2370
+ print(f" Service: {api_key.get('service', 'Unknown')}")
2371
+ print(f" Description: {api_key.get('description', 'No description')}")
2372
+ if api_key.get('example'):
2373
+ print(f" Example: {api_key.get('example')}")
2374
+ if api_key.get('documentation_url'):
2375
+ print(f" Docs: {api_key.get('documentation_url')}")
2376
+ print()
2377
+
2378
+ # Display setup complexity if available
2379
+ if "setupComplexity" in result:
2380
+ complexity = result.get("setupComplexity", "medium")
2381
+ estimated_time = result.get("estimatedSetupTime", "Unknown")
2382
+ print(f"šŸ“Š Setup Complexity: {complexity.upper()}")
2383
+ print(f"ā±ļø Estimated Time: {estimated_time}")
2384
+
2341
2385
  # Print the commands
2342
2386
  print("\nšŸ“‹ Setup Commands:")
2343
2387
  for i, cmd in enumerate(commands, 1):
@@ -2359,6 +2359,29 @@ def get_setup_commands_from_gitingest(repo_url):
2359
2359
  if commands:
2360
2360
  print(f"āœ… Successfully fetched {len(commands)} setup commands from API at {api_url}")
2361
2361
 
2362
+ # Enhanced response handling for API key detection
2363
+ if "requiredApiKeys" in result:
2364
+ api_keys = result.get("requiredApiKeys", [])
2365
+ if api_keys:
2366
+ print(f"\nšŸ”‘ Required API Keys ({len(api_keys)}):")
2367
+ for i, api_key in enumerate(api_keys, 1):
2368
+ status = "šŸ”“ Required" if api_key.get("required", False) else "🟔 Optional"
2369
+ print(f" {i}. {api_key.get('name', 'Unknown')} - {status}")
2370
+ print(f" Service: {api_key.get('service', 'Unknown')}")
2371
+ print(f" Description: {api_key.get('description', 'No description')}")
2372
+ if api_key.get('example'):
2373
+ print(f" Example: {api_key.get('example')}")
2374
+ if api_key.get('documentation_url'):
2375
+ print(f" Docs: {api_key.get('documentation_url')}")
2376
+ print()
2377
+
2378
+ # Display setup complexity if available
2379
+ if "setupComplexity" in result:
2380
+ complexity = result.get("setupComplexity", "medium")
2381
+ estimated_time = result.get("estimatedSetupTime", "Unknown")
2382
+ print(f"šŸ“Š Setup Complexity: {complexity.upper()}")
2383
+ print(f"ā±ļø Estimated Time: {estimated_time}")
2384
+
2362
2385
  # Print the commands
2363
2386
  print("\nšŸ“‹ Setup Commands:")
2364
2387
  for i, cmd in enumerate(commands, 1):