gitarsenal-cli 1.4.3 ā 1.4.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.
- package/package.json +1 -1
- package/python/__pycache__/fetch_modal_tokens.cpython-313.pyc +0 -0
- package/python/fetch_modal_tokens.py +118 -72
- package/python/fix_modal_token.py +9 -4
- package/python/fix_modal_token_advanced.py +5 -0
- package/python/test_import.py +55 -0
- package/python/test_modalSandboxScript.py +295 -41
- package/python/verify_env_vars.py +64 -0
package/package.json
CHANGED
Binary file
|
@@ -2,7 +2,7 @@
|
|
2
2
|
"""
|
3
3
|
Fetch Modal Tokens
|
4
4
|
|
5
|
-
This script fetches Modal tokens from the
|
5
|
+
This script fetches Modal tokens from the proxy server.
|
6
6
|
"""
|
7
7
|
|
8
8
|
import os
|
@@ -12,108 +12,144 @@ import requests
|
|
12
12
|
import subprocess
|
13
13
|
from pathlib import Path
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
def fetch_default_tokens_from_gitarsenal():
|
16
|
+
"""
|
17
|
+
Fetch default Modal tokens from gitarsenal.dev API.
|
18
|
+
|
19
|
+
Returns:
|
20
|
+
tuple: (token_id, token_secret) if successful, (None, None) otherwise
|
21
|
+
"""
|
22
|
+
endpoint = "https://gitarsenal.dev/api/credentials"
|
23
|
+
|
24
|
+
try:
|
25
|
+
headers = {
|
26
|
+
'User-Agent': 'Python-Modal-Token-Fetcher/1.0',
|
27
|
+
'Accept': 'application/json'
|
28
|
+
}
|
29
|
+
|
30
|
+
print(f"š Fetching default tokens from: {endpoint}")
|
31
|
+
|
32
|
+
response = requests.get(endpoint, headers=headers, timeout=30)
|
33
|
+
|
34
|
+
print(f"š Status: {response.status_code}")
|
35
|
+
|
36
|
+
if response.status_code == 200:
|
37
|
+
try:
|
38
|
+
data = response.json()
|
39
|
+
token_id = data.get("modalTokenId")
|
40
|
+
token_secret = data.get("modalTokenSecret")
|
41
|
+
|
42
|
+
if token_id and token_secret:
|
43
|
+
print("ā
Successfully fetched default tokens from gitarsenal.dev")
|
44
|
+
return token_id, token_secret
|
45
|
+
else:
|
46
|
+
print("ā Modal tokens not found in gitarsenal.dev response")
|
47
|
+
return None, None
|
48
|
+
except json.JSONDecodeError:
|
49
|
+
print("ā Invalid JSON response from gitarsenal.dev")
|
50
|
+
return None, None
|
51
|
+
else:
|
52
|
+
print(f"ā Failed to fetch from gitarsenal.dev: {response.status_code} - {response.text[:200]}")
|
53
|
+
return None, None
|
54
|
+
|
55
|
+
except requests.exceptions.Timeout:
|
56
|
+
print("ā Request timeout when fetching from gitarsenal.dev")
|
57
|
+
return None, None
|
58
|
+
except requests.exceptions.ConnectionError:
|
59
|
+
print("ā Connection failed to gitarsenal.dev")
|
60
|
+
return None, None
|
61
|
+
except requests.exceptions.RequestException as e:
|
62
|
+
print(f"ā Request failed to gitarsenal.dev: {e}")
|
63
|
+
return None, None
|
18
64
|
|
19
|
-
def
|
65
|
+
def fetch_tokens_from_proxy(proxy_url=None, api_key=None):
|
20
66
|
"""
|
21
|
-
Fetch
|
67
|
+
Fetch Modal tokens from the proxy server.
|
22
68
|
|
23
69
|
Args:
|
24
|
-
|
70
|
+
proxy_url: URL of the proxy server
|
25
71
|
api_key: API key for authentication
|
26
|
-
credential_id: Optional ID of specific credentials to fetch
|
27
72
|
|
28
73
|
Returns:
|
29
|
-
|
74
|
+
tuple: (token_id, token_secret) if successful, (None, None) otherwise
|
30
75
|
"""
|
31
76
|
# Use environment variables if not provided
|
32
|
-
if not
|
33
|
-
|
34
|
-
if
|
35
|
-
print(f"š Using
|
77
|
+
if not proxy_url:
|
78
|
+
proxy_url = os.environ.get("MODAL_PROXY_URL")
|
79
|
+
if proxy_url:
|
80
|
+
print(f"š Using proxy URL from environment")
|
36
81
|
|
37
82
|
if not api_key:
|
38
|
-
api_key = os.environ.get("
|
83
|
+
api_key = os.environ.get("MODAL_PROXY_API_KEY")
|
39
84
|
if api_key:
|
40
85
|
print(f"š Using API key from environment (length: {len(api_key)})")
|
41
86
|
|
42
87
|
# Check if we have the necessary information
|
88
|
+
if not proxy_url:
|
89
|
+
# print("ā No proxy URL provided or found in environment")
|
90
|
+
print("š” Set MODAL_PROXY_URL environment variable or use --proxy-url argument")
|
91
|
+
return None, None
|
92
|
+
|
43
93
|
if not api_key:
|
44
94
|
print("ā No API key provided or found in environment")
|
45
|
-
print("š” Set
|
46
|
-
return None
|
95
|
+
print("š” Set MODAL_PROXY_API_KEY environment variable or use --proxy-api-key argument")
|
96
|
+
return None, None
|
47
97
|
|
48
98
|
# Ensure the URL ends with a slash
|
49
|
-
if not
|
50
|
-
|
99
|
+
if not proxy_url.endswith("/"):
|
100
|
+
proxy_url += "/"
|
51
101
|
|
52
|
-
# Add the endpoint for fetching
|
53
|
-
|
54
|
-
|
55
|
-
# Add credential ID if provided
|
56
|
-
if credential_id:
|
57
|
-
credentials_url += f"?id={credential_id}"
|
102
|
+
# Add the endpoint for fetching tokens
|
103
|
+
token_url = f"{proxy_url}api/modal-tokens"
|
58
104
|
|
59
105
|
try:
|
60
106
|
# Make the request
|
61
|
-
print(f"š Fetching
|
107
|
+
print(f"š Fetching tokens from proxy server")
|
62
108
|
response = requests.get(
|
63
|
-
|
109
|
+
token_url,
|
64
110
|
headers={"X-API-Key": api_key}
|
65
111
|
)
|
66
112
|
|
67
113
|
# Check if the request was successful
|
68
114
|
if response.status_code == 200:
|
69
115
|
data = response.json()
|
70
|
-
token_id = data.get("
|
71
|
-
token_secret = data.get("
|
72
|
-
openai_api_key = data.get("openaiApiKey")
|
116
|
+
token_id = data.get("token_id")
|
117
|
+
token_secret = data.get("token_secret")
|
73
118
|
|
74
119
|
if token_id and token_secret:
|
75
|
-
print("ā
Successfully fetched
|
76
|
-
return
|
77
|
-
"token_id": token_id,
|
78
|
-
"token_secret": token_secret,
|
79
|
-
"openai_api_key": openai_api_key
|
80
|
-
}
|
120
|
+
print("ā
Successfully fetched tokens from proxy server")
|
121
|
+
return token_id, token_secret
|
81
122
|
else:
|
82
|
-
print("ā
|
83
|
-
return None
|
123
|
+
print("ā Tokens not found in response")
|
124
|
+
return None, None
|
84
125
|
else:
|
85
|
-
print(f"ā Failed to fetch
|
86
|
-
return None
|
126
|
+
print(f"ā Failed to fetch tokens: {response.status_code} - {response.text}")
|
127
|
+
return None, None
|
87
128
|
except Exception as e:
|
88
|
-
print(f"ā Error fetching
|
89
|
-
return None
|
129
|
+
print(f"ā Error fetching tokens: {e}")
|
130
|
+
return None, None
|
90
131
|
|
91
132
|
def get_tokens():
|
92
133
|
"""
|
93
|
-
Get Modal tokens, trying to fetch from the
|
134
|
+
Get Modal tokens, trying to fetch from the proxy server first.
|
94
135
|
Also sets the tokens in environment variables.
|
95
136
|
|
96
137
|
Returns:
|
97
138
|
tuple: (token_id, token_secret)
|
98
139
|
"""
|
99
|
-
# Try to fetch from the
|
100
|
-
|
101
|
-
|
102
|
-
# If we couldn't fetch from the
|
103
|
-
if not
|
104
|
-
print("ā ļø
|
105
|
-
token_id =
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
# Set OpenAI API key if available
|
114
|
-
if openai_api_key:
|
115
|
-
os.environ["OPENAI_API_KEY"] = openai_api_key
|
116
|
-
print(f"ā
Set OPENAI_API_KEY environment variable")
|
140
|
+
# Try to fetch from the proxy server
|
141
|
+
token_id, token_secret = fetch_tokens_from_proxy()
|
142
|
+
|
143
|
+
# If we couldn't fetch from the server, try to get default tokens from gitarsenal.dev
|
144
|
+
if not token_id or not token_secret:
|
145
|
+
print("ā ļø Proxy server failed, trying to fetch default tokens from gitarsenal.dev")
|
146
|
+
token_id, token_secret = fetch_default_tokens_from_gitarsenal()
|
147
|
+
|
148
|
+
# If we still don't have tokens, we can't proceed
|
149
|
+
if not token_id or not token_secret:
|
150
|
+
print("ā Failed to fetch tokens from both proxy server and gitarsenal.dev")
|
151
|
+
print("š” Please check your network connection and API endpoints")
|
152
|
+
return None, None
|
117
153
|
|
118
154
|
# Set the tokens in environment variables
|
119
155
|
os.environ["MODAL_TOKEN_ID"] = token_id
|
@@ -126,23 +162,33 @@ if __name__ == "__main__":
|
|
126
162
|
# Parse command-line arguments if run directly
|
127
163
|
import argparse
|
128
164
|
|
129
|
-
parser = argparse.ArgumentParser(description='Fetch
|
130
|
-
parser.add_argument('--
|
131
|
-
parser.add_argument('--api-key', help='API key for
|
132
|
-
parser.add_argument('--credential-id', help='ID of specific credentials to fetch')
|
165
|
+
parser = argparse.ArgumentParser(description='Fetch Modal tokens from the proxy server')
|
166
|
+
parser.add_argument('--proxy-url', help='URL of the proxy server')
|
167
|
+
parser.add_argument('--proxy-api-key', help='API key for the proxy server')
|
133
168
|
args = parser.parse_args()
|
134
169
|
|
135
|
-
# Set
|
136
|
-
if args.
|
137
|
-
os.environ["
|
138
|
-
print(f"ā
Set
|
170
|
+
# Set proxy URL and API key in environment variables if provided
|
171
|
+
if args.proxy_url:
|
172
|
+
os.environ["MODAL_PROXY_URL"] = args.proxy_url
|
173
|
+
print(f"ā
Set MODAL_PROXY_URL from command line: {args.proxy_url}")
|
139
174
|
|
140
|
-
if args.
|
141
|
-
os.environ["
|
142
|
-
print(f"ā
Set
|
175
|
+
if args.proxy_api_key:
|
176
|
+
os.environ["MODAL_PROXY_API_KEY"] = args.proxy_api_key
|
177
|
+
print(f"ā
Set MODAL_PROXY_API_KEY from command line")
|
143
178
|
|
144
179
|
# Get tokens
|
145
180
|
token_id, token_secret = get_tokens()
|
181
|
+
print(f"Token ID: {token_id}")
|
182
|
+
print(f"Token Secret: {token_secret}")
|
183
|
+
|
184
|
+
# Check if tokens are set in environment variables
|
185
|
+
print(f"\nš DEBUG: Checking environment variables")
|
186
|
+
print(f"š MODAL_TOKEN_ID exists: {'Yes' if os.environ.get('MODAL_TOKEN_ID') else 'No'}")
|
187
|
+
print(f"š MODAL_TOKEN_SECRET exists: {'Yes' if os.environ.get('MODAL_TOKEN_SECRET') else 'No'}")
|
188
|
+
if os.environ.get('MODAL_TOKEN_ID'):
|
189
|
+
print(f"š MODAL_TOKEN_ID length: {len(os.environ.get('MODAL_TOKEN_ID'))}")
|
190
|
+
if os.environ.get('MODAL_TOKEN_SECRET'):
|
191
|
+
print(f"š MODAL_TOKEN_SECRET length: {len(os.environ.get('MODAL_TOKEN_SECRET'))}")
|
146
192
|
|
147
193
|
# Write the tokens to a file for use by other scripts
|
148
194
|
tokens_file = Path(__file__).parent / "modal_tokens.json"
|
@@ -185,4 +231,4 @@ if __name__ == "__main__":
|
|
185
231
|
except Exception as e:
|
186
232
|
print(f"ā Error using Modal CLI: {e}")
|
187
233
|
|
188
|
-
print(f"\nā
All token setup completed successfully")
|
234
|
+
print(f"\nā
All token setup completed successfully")
|
@@ -43,11 +43,16 @@ try:
|
|
43
43
|
# First, try to import the fetch_modal_tokens module
|
44
44
|
from fetch_modal_tokens import get_tokens
|
45
45
|
TOKEN_ID, TOKEN_SECRET = get_tokens()
|
46
|
+
|
47
|
+
# Check if we got valid tokens
|
48
|
+
if TOKEN_ID is None or TOKEN_SECRET is None:
|
49
|
+
raise ValueError("Could not get valid tokens")
|
50
|
+
|
46
51
|
print(f"ā
Using tokens from proxy server or defaults")
|
47
|
-
except ImportError:
|
48
|
-
# If the module is not available, use hardcoded tokens
|
49
|
-
|
50
|
-
|
52
|
+
except (ImportError, ValueError) as e:
|
53
|
+
# If the module is not available or tokens are invalid, use hardcoded tokens
|
54
|
+
TOKEN_ID = "ak-sLhYqCjkvixiYcb9LAuCHp"
|
55
|
+
TOKEN_SECRET = "as-fPzD0Zm0dl6IFAEkhaH9pq" # Real token secret from fr8mafia profile
|
51
56
|
print(f"ā ļø Using default tokens")
|
52
57
|
|
53
58
|
print("š§ Fixing Modal token (basic implementation)...")
|
@@ -28,6 +28,8 @@ try:
|
|
28
28
|
except ImportError:
|
29
29
|
# If the module is not available, use hardcoded tokens
|
30
30
|
# print(f"ā ļø Using default tokens")
|
31
|
+
TOKEN_ID = "ak-sLhYqCjkvixiYcb9LAuCHp"
|
32
|
+
TOKEN_SECRET = "as-fPzD0Zm0dl6IFAEkhaH9pq"
|
31
33
|
|
32
34
|
# print("š§ Advanced Modal Token Fixer")
|
33
35
|
|
@@ -90,10 +92,13 @@ try:
|
|
90
92
|
)
|
91
93
|
|
92
94
|
if result.returncode == 0:
|
95
|
+
pass
|
93
96
|
# print(f"ā
Successfully set token via Modal CLI")
|
94
97
|
else:
|
98
|
+
pass
|
95
99
|
# print(f"ā Failed to set token via Modal CLI: {result.stderr}")
|
96
100
|
except Exception as e:
|
101
|
+
pass
|
97
102
|
# print(f"ā Error using Modal CLI: {e}")
|
98
103
|
|
99
104
|
# Approach 4: Use Modal Python API to set token
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Test importing and using get_tokens from fetch_modal_tokens
|
4
|
+
"""
|
5
|
+
|
6
|
+
import os
|
7
|
+
import sys
|
8
|
+
|
9
|
+
def main():
|
10
|
+
"""Main function to test importing and using get_tokens"""
|
11
|
+
print("š Testing import of get_tokens from fetch_modal_tokens...")
|
12
|
+
|
13
|
+
# Set a test API key for the script to use
|
14
|
+
os.environ["GITARSENAL_API_KEY"] = "test_key"
|
15
|
+
|
16
|
+
try:
|
17
|
+
# Import the function
|
18
|
+
from fetch_modal_tokens import get_tokens
|
19
|
+
print("ā
Successfully imported get_tokens from fetch_modal_tokens")
|
20
|
+
|
21
|
+
# Call the function
|
22
|
+
print("š Calling get_tokens()...")
|
23
|
+
token_id, token_secret = get_tokens()
|
24
|
+
|
25
|
+
# Check the results
|
26
|
+
print("ā
Successfully called get_tokens()")
|
27
|
+
print(f" token_id: {token_id[:5]}...{token_id[-5:]}")
|
28
|
+
print(f" token_secret: {token_secret[:5]}...{token_secret[-5:]}")
|
29
|
+
|
30
|
+
# Check if environment variables were set
|
31
|
+
if os.environ.get("MODAL_TOKEN_ID") == token_id:
|
32
|
+
print("ā
MODAL_TOKEN_ID environment variable set correctly")
|
33
|
+
else:
|
34
|
+
print("ā MODAL_TOKEN_ID environment variable not set correctly")
|
35
|
+
|
36
|
+
if os.environ.get("MODAL_TOKEN_SECRET") == token_secret:
|
37
|
+
print("ā
MODAL_TOKEN_SECRET environment variable set correctly")
|
38
|
+
else:
|
39
|
+
print("ā MODAL_TOKEN_SECRET environment variable not set correctly")
|
40
|
+
|
41
|
+
# Check if OPENAI_API_KEY was set
|
42
|
+
if os.environ.get("OPENAI_API_KEY"):
|
43
|
+
print("ā
OPENAI_API_KEY environment variable set")
|
44
|
+
print(f" length: {len(os.environ.get('OPENAI_API_KEY'))}")
|
45
|
+
else:
|
46
|
+
print("ā OPENAI_API_KEY environment variable not set")
|
47
|
+
|
48
|
+
return True
|
49
|
+
except Exception as e:
|
50
|
+
print(f"ā Error: {e}")
|
51
|
+
return False
|
52
|
+
|
53
|
+
if __name__ == "__main__":
|
54
|
+
success = main()
|
55
|
+
sys.exit(0 if success else 1)
|
@@ -39,6 +39,11 @@ try:
|
|
39
39
|
# print("š Fetching tokens from proxy server...")
|
40
40
|
from fetch_modal_tokens import get_tokens
|
41
41
|
token_id, token_secret = get_tokens()
|
42
|
+
|
43
|
+
# Check if we got valid tokens
|
44
|
+
if token_id is None or token_secret is None:
|
45
|
+
raise ValueError("Could not get valid tokens")
|
46
|
+
|
42
47
|
# print(f"ā
Tokens fetched successfully")
|
43
48
|
|
44
49
|
# Explicitly set the environment variables again to be sure
|
@@ -351,22 +356,48 @@ def call_openai_for_debug(command, error_output, api_key=None, current_dir=None,
|
|
351
356
|
print("ā ļø Error output is empty. Cannot effectively debug the command.")
|
352
357
|
print("ā ļø Skipping OpenAI debugging due to lack of error information.")
|
353
358
|
return None
|
354
|
-
|
359
|
+
|
360
|
+
# Try to get API key from multiple sources
|
355
361
|
if not api_key:
|
356
|
-
#
|
362
|
+
# First try environment variable
|
357
363
|
api_key = os.environ.get("OPENAI_API_KEY")
|
358
364
|
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
365
|
+
# Store the API key in a persistent file if found
|
366
|
+
if api_key:
|
367
|
+
try:
|
368
|
+
os.makedirs(os.path.expanduser("~/.gitarsenal"), exist_ok=True)
|
369
|
+
with open(os.path.expanduser("~/.gitarsenal/openai_key"), "w") as f:
|
370
|
+
f.write(api_key)
|
371
|
+
print("ā
Saved OpenAI API key for future use")
|
372
|
+
except Exception as e:
|
373
|
+
print(f"ā ļø Could not save API key: {e}")
|
374
|
+
|
375
|
+
# Try to load from saved file if not in environment
|
376
|
+
if not api_key:
|
377
|
+
try:
|
378
|
+
key_file = os.path.expanduser("~/.gitarsenal/openai_key")
|
379
|
+
if os.path.exists(key_file):
|
380
|
+
with open(key_file, "r") as f:
|
381
|
+
api_key = f.read().strip()
|
382
|
+
if api_key:
|
383
|
+
print("ā
Loaded OpenAI API key from saved file")
|
384
|
+
# Also set in environment for this session
|
385
|
+
os.environ["OPENAI_API_KEY"] = api_key
|
386
|
+
except Exception as e:
|
387
|
+
print(f"ā ļø Could not load saved API key: {e}")
|
388
|
+
|
389
|
+
# Then try credentials manager
|
390
|
+
if not api_key:
|
391
|
+
try:
|
392
|
+
from credentials_manager import CredentialsManager
|
393
|
+
credentials_manager = CredentialsManager()
|
394
|
+
api_key = credentials_manager.get_openai_api_key()
|
395
|
+
except ImportError:
|
396
|
+
# Fall back to direct input if credentials_manager is not available
|
397
|
+
pass
|
398
|
+
|
399
|
+
# Finally, prompt the user if still no API key
|
400
|
+
if not api_key:
|
370
401
|
print("\n" + "="*60)
|
371
402
|
print("š OPENAI API KEY REQUIRED FOR DEBUGGING")
|
372
403
|
print("="*60)
|
@@ -381,6 +412,8 @@ def call_openai_for_debug(command, error_output, api_key=None, current_dir=None,
|
|
381
412
|
print("ā No API key provided. Skipping debugging.")
|
382
413
|
return None
|
383
414
|
print("ā
API key received successfully!")
|
415
|
+
# Save the API key to environment for future use in this session
|
416
|
+
os.environ["OPENAI_API_KEY"] = api_key
|
384
417
|
except KeyboardInterrupt:
|
385
418
|
print("\nā API key input cancelled by user.")
|
386
419
|
return None
|
@@ -388,9 +421,11 @@ def call_openai_for_debug(command, error_output, api_key=None, current_dir=None,
|
|
388
421
|
print(f"ā Error getting API key: {e}")
|
389
422
|
return None
|
390
423
|
|
391
|
-
#
|
424
|
+
# Gather additional context to help with debugging
|
392
425
|
directory_context = ""
|
393
426
|
system_info = ""
|
427
|
+
command_history = ""
|
428
|
+
file_context = ""
|
394
429
|
|
395
430
|
if sandbox:
|
396
431
|
try:
|
@@ -404,6 +439,7 @@ def call_openai_for_debug(command, error_output, api_key=None, current_dir=None,
|
|
404
439
|
uname -a
|
405
440
|
echo -e "\nPython Information:"
|
406
441
|
python --version
|
442
|
+
pip --version
|
407
443
|
echo -e "\nPackage Manager:"
|
408
444
|
which apt 2>/dev/null && echo "apt available" || echo "apt not available"
|
409
445
|
which yum 2>/dev/null && echo "yum available" || echo "yum not available"
|
@@ -458,6 +494,72 @@ Directory contents:
|
|
458
494
|
{parent_context}
|
459
495
|
"""
|
460
496
|
print("ā
Directory context gathered successfully")
|
497
|
+
|
498
|
+
# Check for relevant files that might provide additional context
|
499
|
+
# For example, if error mentions a specific file, try to get its content
|
500
|
+
relevant_files = []
|
501
|
+
error_files = re.findall(r'(?:No such file or directory|cannot open|not found): ([^\s:]+)', error_output)
|
502
|
+
if error_files:
|
503
|
+
for file_path in error_files:
|
504
|
+
# Clean up the file path
|
505
|
+
file_path = file_path.strip("'\"")
|
506
|
+
if not os.path.isabs(file_path):
|
507
|
+
file_path = os.path.join(current_dir, file_path)
|
508
|
+
|
509
|
+
# Try to get the parent directory if the file doesn't exist
|
510
|
+
if '/' in file_path:
|
511
|
+
parent_file_dir = os.path.dirname(file_path)
|
512
|
+
relevant_files.append(parent_file_dir)
|
513
|
+
|
514
|
+
# Look for package.json, requirements.txt, etc.
|
515
|
+
common_config_files = ["package.json", "requirements.txt", "pyproject.toml", "setup.py",
|
516
|
+
"Pipfile", "Dockerfile", "docker-compose.yml", "Makefile"]
|
517
|
+
|
518
|
+
for config_file in common_config_files:
|
519
|
+
check_cmd = f"test -f {current_dir}/{config_file}"
|
520
|
+
check_result = sandbox.exec("bash", "-c", check_cmd)
|
521
|
+
check_result.wait()
|
522
|
+
if check_result.returncode == 0:
|
523
|
+
relevant_files.append(f"{current_dir}/{config_file}")
|
524
|
+
|
525
|
+
# Get content of relevant files
|
526
|
+
if relevant_files:
|
527
|
+
file_context = "\nRelevant file contents:\n"
|
528
|
+
for file_path in relevant_files[:2]: # Limit to 2 files to avoid too much context
|
529
|
+
try:
|
530
|
+
file_check_cmd = f"test -f {file_path}"
|
531
|
+
file_check = sandbox.exec("bash", "-c", file_check_cmd)
|
532
|
+
file_check.wait()
|
533
|
+
|
534
|
+
if file_check.returncode == 0:
|
535
|
+
# It's a file, get its content
|
536
|
+
cat_cmd = f"cat {file_path}"
|
537
|
+
cat_result = sandbox.exec("bash", "-c", cat_cmd)
|
538
|
+
file_content = ""
|
539
|
+
for line in cat_result.stdout:
|
540
|
+
file_content += _to_str(line)
|
541
|
+
cat_result.wait()
|
542
|
+
|
543
|
+
# Truncate if too long
|
544
|
+
if len(file_content) > 1000:
|
545
|
+
file_content = file_content[:1000] + "\n... (truncated)"
|
546
|
+
|
547
|
+
file_context += f"\n--- {file_path} ---\n{file_content}\n"
|
548
|
+
else:
|
549
|
+
# It's a directory, list its contents
|
550
|
+
ls_cmd = f"ls -la {file_path}"
|
551
|
+
ls_dir_result = sandbox.exec("bash", "-c", ls_cmd)
|
552
|
+
dir_content = ""
|
553
|
+
for line in ls_dir_result.stdout:
|
554
|
+
dir_content += _to_str(line)
|
555
|
+
ls_dir_result.wait()
|
556
|
+
|
557
|
+
file_context += f"\n--- Directory: {file_path} ---\n{dir_content}\n"
|
558
|
+
except Exception as e:
|
559
|
+
print(f"ā ļø Error getting content of {file_path}: {e}")
|
560
|
+
|
561
|
+
print(f"ā
Additional file context gathered from {len(relevant_files)} relevant files")
|
562
|
+
|
461
563
|
except Exception as e:
|
462
564
|
print(f"ā ļø Error getting directory context: {e}")
|
463
565
|
directory_context = f"\nCurrent directory: {current_dir}\n"
|
@@ -489,6 +591,8 @@ But it failed with this error:
|
|
489
591
|
```
|
490
592
|
{system_info}
|
491
593
|
{directory_context}
|
594
|
+
{file_context}
|
595
|
+
|
492
596
|
Please analyze the error and provide ONLY a single terminal command that would fix the issue.
|
493
597
|
Consider the current directory, system information, and directory contents carefully before suggesting a solution.
|
494
598
|
|
@@ -502,29 +606,127 @@ Do not provide any explanations, just the exact command to run.
|
|
502
606
|
"""
|
503
607
|
|
504
608
|
# Prepare the API request payload
|
505
|
-
|
506
|
-
|
507
|
-
"
|
508
|
-
|
509
|
-
{"role": "user", "content": prompt}
|
510
|
-
],
|
511
|
-
"temperature": 0.2,
|
512
|
-
"max_tokens": 300
|
513
|
-
}
|
609
|
+
# Try to use GPT-4 first, but fall back to other models if needed
|
610
|
+
models_to_try = [
|
611
|
+
"gpt-4o-mini", # First choice: GPT-4o (most widely available)
|
612
|
+
]
|
514
613
|
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
timeout=30
|
522
|
-
)
|
614
|
+
# Check if we have a preferred model in environment
|
615
|
+
preferred_model = os.environ.get("OPENAI_MODEL")
|
616
|
+
if preferred_model:
|
617
|
+
# Insert the preferred model at the beginning of the list
|
618
|
+
models_to_try.insert(0, preferred_model)
|
619
|
+
print(f"ā
Using preferred model from environment: {preferred_model}")
|
523
620
|
|
524
|
-
|
525
|
-
|
621
|
+
# Remove duplicates while preserving order
|
622
|
+
models_to_try = list(dict.fromkeys(models_to_try))
|
623
|
+
|
624
|
+
# Function to make the API call with a specific model
|
625
|
+
def try_api_call(model_name, retries=2, backoff_factor=1.5):
|
626
|
+
payload = {
|
627
|
+
"model": model_name,
|
628
|
+
"messages": [
|
629
|
+
{"role": "system", "content": "You are a debugging assistant. Provide only the terminal command to fix the issue, analyze the issue first understand why its happening and then provide the command to fix the issue. If you see missing pytest errors, suggest 'pip install pytest'. For wandb login issues, suggest 'wandb login YOUR_API_KEY' and the system will handle prompting for the actual key."},
|
630
|
+
{"role": "user", "content": prompt}
|
631
|
+
],
|
632
|
+
"temperature": 0.2,
|
633
|
+
"max_tokens": 300
|
634
|
+
}
|
635
|
+
|
636
|
+
# Add specific handling for common errors
|
637
|
+
last_error = None
|
638
|
+
for attempt in range(retries + 1):
|
639
|
+
try:
|
640
|
+
if attempt > 0:
|
641
|
+
# Exponential backoff
|
642
|
+
wait_time = backoff_factor * (2 ** (attempt - 1))
|
643
|
+
print(f"ā±ļø Retrying in {wait_time:.1f} seconds... (attempt {attempt+1}/{retries+1})")
|
644
|
+
time.sleep(wait_time)
|
645
|
+
|
646
|
+
print(f"š¤ Calling OpenAI with {model_name} model to debug the failed command...")
|
647
|
+
response = requests.post(
|
648
|
+
"https://api.openai.com/v1/chat/completions",
|
649
|
+
headers=headers,
|
650
|
+
json=payload,
|
651
|
+
timeout=45 # Increased timeout for reliability
|
652
|
+
)
|
653
|
+
|
654
|
+
# Handle specific status codes
|
655
|
+
if response.status_code == 200:
|
656
|
+
return response.json(), None
|
657
|
+
elif response.status_code == 401:
|
658
|
+
error_msg = "Authentication error: Invalid API key"
|
659
|
+
print(f"ā {error_msg}")
|
660
|
+
# Don't retry auth errors
|
661
|
+
return None, error_msg
|
662
|
+
elif response.status_code == 429:
|
663
|
+
error_msg = "Rate limit exceeded or quota reached"
|
664
|
+
print(f"ā ļø {error_msg}")
|
665
|
+
# Always retry rate limit errors with increasing backoff
|
666
|
+
last_error = error_msg
|
667
|
+
continue
|
668
|
+
elif response.status_code == 500:
|
669
|
+
error_msg = "OpenAI server error"
|
670
|
+
print(f"ā ļø {error_msg}")
|
671
|
+
# Retry server errors
|
672
|
+
last_error = error_msg
|
673
|
+
continue
|
674
|
+
else:
|
675
|
+
error_msg = f"Status code: {response.status_code}, Response: {response.text}"
|
676
|
+
print(f"ā ļø OpenAI API error: {error_msg}")
|
677
|
+
last_error = error_msg
|
678
|
+
# Only retry if we have attempts left
|
679
|
+
if attempt < retries:
|
680
|
+
continue
|
681
|
+
return None, error_msg
|
682
|
+
except requests.exceptions.Timeout:
|
683
|
+
error_msg = "Request timed out"
|
684
|
+
print(f"ā ļø {error_msg}")
|
685
|
+
last_error = error_msg
|
686
|
+
# Always retry timeouts
|
687
|
+
continue
|
688
|
+
except requests.exceptions.ConnectionError:
|
689
|
+
error_msg = "Connection error"
|
690
|
+
print(f"ā ļø {error_msg}")
|
691
|
+
last_error = error_msg
|
692
|
+
# Always retry connection errors
|
693
|
+
continue
|
694
|
+
except Exception as e:
|
695
|
+
error_msg = str(e)
|
696
|
+
print(f"ā ļø Unexpected error: {error_msg}")
|
697
|
+
last_error = error_msg
|
698
|
+
# Only retry if we have attempts left
|
699
|
+
if attempt < retries:
|
700
|
+
continue
|
701
|
+
return None, error_msg
|
702
|
+
|
703
|
+
# If we get here, all retries failed
|
704
|
+
return None, last_error
|
705
|
+
|
706
|
+
# Try each model in sequence until one works
|
707
|
+
result = None
|
708
|
+
last_error = None
|
709
|
+
|
710
|
+
for model in models_to_try:
|
711
|
+
result, error = try_api_call(model)
|
712
|
+
if result:
|
713
|
+
print(f"ā
Successfully got response from {model}")
|
714
|
+
break
|
715
|
+
else:
|
716
|
+
print(f"ā ļø Failed to get response from {model}: {error}")
|
717
|
+
last_error = error
|
718
|
+
|
719
|
+
if not result:
|
720
|
+
print(f"ā All model attempts failed. Last error: {last_error}")
|
721
|
+
return None
|
722
|
+
|
723
|
+
# Process the response
|
724
|
+
try:
|
526
725
|
fix_command = result["choices"][0]["message"]["content"].strip()
|
527
726
|
|
727
|
+
# Save the original response for debugging
|
728
|
+
original_response = fix_command
|
729
|
+
|
528
730
|
# Extract just the command if it's wrapped in backticks or explanation
|
529
731
|
if "```" in fix_command:
|
530
732
|
# Extract content between backticks
|
@@ -532,21 +734,73 @@ Do not provide any explanations, just the exact command to run.
|
|
532
734
|
code_blocks = re.findall(r'```(?:bash|sh)?\s*(.*?)\s*```', fix_command, re.DOTALL)
|
533
735
|
if code_blocks:
|
534
736
|
fix_command = code_blocks[0].strip()
|
737
|
+
print(f"ā
Extracted command from code block: {fix_command}")
|
535
738
|
|
536
739
|
# If the response still has explanatory text, try to extract just the command
|
537
740
|
if len(fix_command.split('\n')) > 1:
|
538
|
-
#
|
539
|
-
|
540
|
-
|
541
|
-
|
741
|
+
# First try to find lines that look like commands (start with common command prefixes)
|
742
|
+
command_prefixes = ['sudo', 'apt', 'pip', 'npm', 'yarn', 'git', 'cd', 'mv', 'cp', 'rm', 'mkdir', 'touch',
|
743
|
+
'chmod', 'chown', 'echo', 'cat', 'python', 'python3', 'node', 'export',
|
744
|
+
'curl', 'wget', 'docker', 'make', 'gcc', 'g++', 'javac', 'java',
|
745
|
+
'conda', 'uv', 'poetry', 'nvm', 'rbenv', 'pyenv', 'rustup']
|
746
|
+
|
747
|
+
# Check for lines that start with common command prefixes
|
748
|
+
command_lines = [line.strip() for line in fix_command.split('\n')
|
749
|
+
if any(line.strip().startswith(prefix) for prefix in command_prefixes)]
|
750
|
+
|
751
|
+
if command_lines:
|
752
|
+
# Use the first command line found
|
753
|
+
fix_command = command_lines[0]
|
754
|
+
print(f"ā
Identified command by prefix: {fix_command}")
|
755
|
+
else:
|
756
|
+
# Try to find lines that look like commands (contain common shell patterns)
|
757
|
+
shell_patterns = [' | ', ' > ', ' >> ', ' && ', ' || ', ' ; ', '$(', '`', ' -y ', ' --yes ']
|
758
|
+
command_lines = [line.strip() for line in fix_command.split('\n')
|
759
|
+
if any(pattern in line for pattern in shell_patterns)]
|
760
|
+
|
761
|
+
if command_lines:
|
762
|
+
# Use the first command line found
|
763
|
+
fix_command = command_lines[0]
|
764
|
+
print(f"ā
Identified command by shell pattern: {fix_command}")
|
765
|
+
else:
|
766
|
+
# Fall back to the shortest non-empty line as it's likely the command
|
767
|
+
lines = [line.strip() for line in fix_command.split('\n') if line.strip()]
|
768
|
+
if lines:
|
769
|
+
# Exclude very short lines that are likely not commands
|
770
|
+
valid_lines = [line for line in lines if len(line) > 5]
|
771
|
+
if valid_lines:
|
772
|
+
fix_command = min(valid_lines, key=len)
|
773
|
+
else:
|
774
|
+
fix_command = min(lines, key=len)
|
775
|
+
print(f"ā
Selected shortest line as command: {fix_command}")
|
776
|
+
|
777
|
+
# Clean up the command - remove any trailing periods or quotes
|
778
|
+
fix_command = fix_command.rstrip('.;"\'')
|
779
|
+
|
780
|
+
# Remove common prefixes that LLMs sometimes add
|
781
|
+
prefixes_to_remove = [
|
782
|
+
"Run: ", "Execute: ", "Try: ", "Command: ", "Fix: ", "Solution: ",
|
783
|
+
"You should run: ", "You can run: ", "You need to run: "
|
784
|
+
]
|
785
|
+
for prefix in prefixes_to_remove:
|
786
|
+
if fix_command.startswith(prefix):
|
787
|
+
fix_command = fix_command[len(prefix):].strip()
|
788
|
+
print(f"ā
Removed prefix: {prefix}")
|
789
|
+
break
|
790
|
+
|
791
|
+
# If the command is still multi-line or very long, it might not be a valid command
|
792
|
+
if len(fix_command.split('\n')) > 1 or len(fix_command) > 500:
|
793
|
+
print("ā ļø Extracted command appears invalid (multi-line or too long)")
|
794
|
+
print("š Original response from LLM:")
|
795
|
+
print("-" * 60)
|
796
|
+
print(original_response)
|
797
|
+
print("-" * 60)
|
798
|
+
print("ā ļø Using best guess for command")
|
542
799
|
|
543
800
|
print(f"š§ Suggested fix: {fix_command}")
|
544
801
|
return fix_command
|
545
|
-
else:
|
546
|
-
print(f"ā OpenAI API error: {response.status_code} - {response.text}")
|
547
|
-
return None
|
548
802
|
except Exception as e:
|
549
|
-
print(f"ā Error
|
803
|
+
print(f"ā Error processing OpenAI response: {e}")
|
550
804
|
return None
|
551
805
|
|
552
806
|
def prompt_for_hf_token():
|
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Verify that environment variables are set correctly after running fetch_modal_tokens.py
|
4
|
+
"""
|
5
|
+
|
6
|
+
import os
|
7
|
+
import subprocess
|
8
|
+
import sys
|
9
|
+
|
10
|
+
def check_env_var(var_name):
|
11
|
+
"""Check if an environment variable is set and print its status"""
|
12
|
+
value = os.environ.get(var_name)
|
13
|
+
if value:
|
14
|
+
print(f"ā
{var_name} is set (length: {len(value)})")
|
15
|
+
return True
|
16
|
+
else:
|
17
|
+
print(f"ā {var_name} is not set")
|
18
|
+
return False
|
19
|
+
|
20
|
+
def main():
|
21
|
+
"""Main function to verify environment variables"""
|
22
|
+
print("š Checking environment variables before running fetch_modal_tokens.py...")
|
23
|
+
modal_id_before = check_env_var("MODAL_TOKEN_ID")
|
24
|
+
modal_secret_before = check_env_var("MODAL_TOKEN_SECRET")
|
25
|
+
openai_key_before = check_env_var("OPENAI_API_KEY")
|
26
|
+
|
27
|
+
print("\nš Running fetch_modal_tokens.py...")
|
28
|
+
# Set a test API key for the script to use
|
29
|
+
os.environ["GITARSENAL_API_KEY"] = "test_key"
|
30
|
+
|
31
|
+
try:
|
32
|
+
# Import the module to run it
|
33
|
+
from fetch_modal_tokens import get_tokens
|
34
|
+
token_id, token_secret = get_tokens()
|
35
|
+
print(f"\nā
get_tokens() returned values successfully")
|
36
|
+
print(f" token_id length: {len(token_id) if token_id else 0}")
|
37
|
+
print(f" token_secret length: {len(token_secret) if token_secret else 0}")
|
38
|
+
except Exception as e:
|
39
|
+
print(f"\nā Error running get_tokens(): {e}")
|
40
|
+
sys.exit(1)
|
41
|
+
|
42
|
+
print("\nš Checking environment variables after running fetch_modal_tokens.py...")
|
43
|
+
modal_id_after = check_env_var("MODAL_TOKEN_ID")
|
44
|
+
modal_secret_after = check_env_var("MODAL_TOKEN_SECRET")
|
45
|
+
openai_key_after = check_env_var("OPENAI_API_KEY")
|
46
|
+
|
47
|
+
# Verify that the variables were set
|
48
|
+
if modal_id_after and modal_secret_after:
|
49
|
+
print("\nā
MODAL_TOKEN_ID and MODAL_TOKEN_SECRET were successfully set")
|
50
|
+
else:
|
51
|
+
print("\nā Failed to set all required environment variables")
|
52
|
+
|
53
|
+
if openai_key_after:
|
54
|
+
print("ā
OPENAI_API_KEY was also set")
|
55
|
+
|
56
|
+
# Check if the values match what was returned by get_tokens()
|
57
|
+
if modal_id_after and os.environ.get("MODAL_TOKEN_ID") == token_id:
|
58
|
+
print("ā
MODAL_TOKEN_ID matches the value returned by get_tokens()")
|
59
|
+
|
60
|
+
if modal_secret_after and os.environ.get("MODAL_TOKEN_SECRET") == token_secret:
|
61
|
+
print("ā
MODAL_TOKEN_SECRET matches the value returned by get_tokens()")
|
62
|
+
|
63
|
+
if __name__ == "__main__":
|
64
|
+
main()
|