gitarsenal-cli 1.6.11 โ 1.6.12
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 (
|
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: '
|
280
|
+
type: 'input',
|
281
281
|
name: 'service',
|
282
|
-
message: '
|
283
|
-
|
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: '
|
375
|
+
type: 'input',
|
381
376
|
name: 'service',
|
382
|
-
message: '
|
383
|
-
|
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: '
|
419
|
+
type: 'input',
|
430
420
|
name: 'service',
|
431
|
-
message: '
|
432
|
-
|
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
@@ -41,23 +41,51 @@ def handle_add(credentials_manager, args):
|
|
41
41
|
sys.exit(1)
|
42
42
|
|
43
43
|
if not key:
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
56
|
-
|
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
|
-
|
97
|
-
|
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
|
-
#
|
110
|
-
|
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
|
118
|
-
|
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
|
-
#
|
145
|
-
|
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
|
153
|
-
|
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:
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Test script to verify dynamic service support
|
4
|
+
"""
|
5
|
+
|
6
|
+
import sys
|
7
|
+
import os
|
8
|
+
from gitarsenal_keys import main
|
9
|
+
|
10
|
+
def test_dynamic_services():
|
11
|
+
print("๐งช Testing GitArsenal Dynamic Service Support")
|
12
|
+
print("=" * 60)
|
13
|
+
|
14
|
+
# Test adding a new service (claude)
|
15
|
+
print("\n1. Testing Claude service addition...")
|
16
|
+
sys.argv = ['gitarsenal_keys.py', 'add', '--service', 'claude', '--key', 'sk-test-claude-key']
|
17
|
+
|
18
|
+
try:
|
19
|
+
main()
|
20
|
+
print("โ
Claude service test completed")
|
21
|
+
except SystemExit as e:
|
22
|
+
if e.code == 0:
|
23
|
+
print("โ
Claude service test completed successfully")
|
24
|
+
else:
|
25
|
+
print(f"โ Claude service test failed with exit code {e.code}")
|
26
|
+
|
27
|
+
# Test adding another new service (custom-service)
|
28
|
+
print("\n2. Testing custom service addition...")
|
29
|
+
sys.argv = ['gitarsenal_keys.py', 'add', '--service', 'custom-service', '--key', 'custom-key-123']
|
30
|
+
|
31
|
+
try:
|
32
|
+
main()
|
33
|
+
print("โ
Custom service test completed")
|
34
|
+
except SystemExit as e:
|
35
|
+
if e.code == 0:
|
36
|
+
print("โ
Custom service test completed successfully")
|
37
|
+
else:
|
38
|
+
print(f"โ Custom service test failed with exit code {e.code}")
|
39
|
+
|
40
|
+
# Test listing to see all services
|
41
|
+
print("\n3. Testing service listing...")
|
42
|
+
sys.argv = ['gitarsenal_keys.py', 'list']
|
43
|
+
|
44
|
+
try:
|
45
|
+
main()
|
46
|
+
print("โ
Service listing test completed")
|
47
|
+
except SystemExit as e:
|
48
|
+
if e.code == 0:
|
49
|
+
print("โ
Service listing test completed successfully")
|
50
|
+
else:
|
51
|
+
print(f"โ Service listing test failed with exit code {e.code}")
|
52
|
+
|
53
|
+
# Test viewing a specific service
|
54
|
+
print("\n4. Testing service viewing...")
|
55
|
+
sys.argv = ['gitarsenal_keys.py', 'view', '--service', 'claude']
|
56
|
+
|
57
|
+
try:
|
58
|
+
main()
|
59
|
+
print("โ
Service viewing test completed")
|
60
|
+
except SystemExit as e:
|
61
|
+
if e.code == 0:
|
62
|
+
print("โ
Service viewing test completed successfully")
|
63
|
+
else:
|
64
|
+
print(f"โ Service viewing test failed with exit code {e.code}")
|
65
|
+
|
66
|
+
if __name__ == "__main__":
|
67
|
+
test_dynamic_services()
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Test script to verify interactive key prompting functionality
|
4
|
+
"""
|
5
|
+
|
6
|
+
import sys
|
7
|
+
import os
|
8
|
+
from gitarsenal_keys import main
|
9
|
+
|
10
|
+
def test_interactive_prompting():
|
11
|
+
print("๐งช Testing GitArsenal Interactive Key Prompting")
|
12
|
+
print("=" * 60)
|
13
|
+
|
14
|
+
# Test that the script can be called with just service
|
15
|
+
print("\n1. Testing interactive prompting...")
|
16
|
+
print(" This should prompt for the API key when only service is provided")
|
17
|
+
|
18
|
+
# Simulate command line arguments
|
19
|
+
sys.argv = ['gitarsenal_keys.py', 'add', '--service', 'huggingface']
|
20
|
+
|
21
|
+
try:
|
22
|
+
# This will prompt for the key interactively
|
23
|
+
main()
|
24
|
+
print("โ
Interactive prompting test completed")
|
25
|
+
except SystemExit as e:
|
26
|
+
if e.code == 0:
|
27
|
+
print("โ
Interactive prompting test completed successfully")
|
28
|
+
else:
|
29
|
+
print(f"โ Interactive prompting test failed with exit code {e.code}")
|
30
|
+
except Exception as e:
|
31
|
+
print(f"โ Interactive prompting test failed with error: {e}")
|
32
|
+
|
33
|
+
if __name__ == "__main__":
|
34
|
+
test_interactive_prompting()
|
@@ -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
|
421
|
+
# Then try credentials manager
|
422
422
|
if not api_key:
|
423
|
-
print("๐ DEBUG: Trying credentials manager
|
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.
|
427
|
+
api_key = credentials_manager.get_openai_api_key()
|
428
428
|
if api_key:
|
429
|
-
print(f"๐ DEBUG:
|
430
|
-
print(f"๐ DEBUG:
|
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:
|
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
|
-
#
|
440
|
+
# Finally, prompt the user if still no API key
|
441
441
|
if not api_key:
|
442
|
-
print("๐ DEBUG: No
|
443
|
-
print("
|
444
|
-
|
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
|
449
|
-
print("๐ก
|
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"โ
|
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 = ""
|