certora-prover-cli 0.1.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.
- certora_prover_cli-0.1.0.dist-info/METADATA +293 -0
- certora_prover_cli-0.1.0.dist-info/RECORD +28 -0
- certora_prover_cli-0.1.0.dist-info/WHEEL +4 -0
- certora_prover_cli-0.1.0.dist-info/entry_points.txt +3 -0
- certora_prover_cli-0.1.0.dist-info/licenses/LICENSE +674 -0
- prover_output_utility/__init__.py +42 -0
- prover_output_utility/_version.py +24 -0
- prover_output_utility/api/__init__.py +14 -0
- prover_output_utility/api/aws_data_fetcher.py +485 -0
- prover_output_utility/api/base_data_fetcher.py +176 -0
- prover_output_utility/api/cache.py +149 -0
- prover_output_utility/api/data_fetcher.py +628 -0
- prover_output_utility/api/local_data_fetcher.py +302 -0
- prover_output_utility/api/main.py +1333 -0
- prover_output_utility/api/tree_parser.py +420 -0
- prover_output_utility/api/url_utils.py +125 -0
- prover_output_utility/auth.py +172 -0
- prover_output_utility/aws_auth.py +124 -0
- prover_output_utility/breadcrumb.py +358 -0
- prover_output_utility/exceptions.py +43 -0
- prover_output_utility/group_summary.py +335 -0
- prover_output_utility/job_report.py +115 -0
- prover_output_utility/models.py +689 -0
- prover_output_utility/parsers.py +212 -0
- prover_output_utility/prover_api.py +37 -0
- prover_output_utility/prover_cli.py +682 -0
- prover_output_utility/py.typed +0 -0
- prover_output_utility/sighash.py +345 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Certora Ltd.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
ProverCLI - Python API for parsing Certora Prover outputs.
|
|
7
|
+
|
|
8
|
+
This module provides an easy-to-use API for fetching and parsing outputs from the Certora Prover.
|
|
9
|
+
Supports both job URLs and job IDs as input.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .api.data_fetcher import default_api_base_url
|
|
13
|
+
from .auth import cloud_server_for_env, prover_frontend_url, resolve_login_env
|
|
14
|
+
from .prover_api import ProverOutputAPI
|
|
15
|
+
from .models import (
|
|
16
|
+
AlertType,
|
|
17
|
+
AssertType,
|
|
18
|
+
CheckResult,
|
|
19
|
+
JobStatus,
|
|
20
|
+
NodeStatus,
|
|
21
|
+
NodeType,
|
|
22
|
+
ParsedAlert,
|
|
23
|
+
)
|
|
24
|
+
from .job_report import JobAnalyzer, JobReport
|
|
25
|
+
|
|
26
|
+
__version__ = "1.0.0"
|
|
27
|
+
__all__ = [
|
|
28
|
+
"ProverOutputAPI",
|
|
29
|
+
"AlertType",
|
|
30
|
+
"AssertType",
|
|
31
|
+
"CheckResult",
|
|
32
|
+
"JobStatus",
|
|
33
|
+
"NodeStatus",
|
|
34
|
+
"NodeType",
|
|
35
|
+
"ParsedAlert",
|
|
36
|
+
"JobAnalyzer",
|
|
37
|
+
"JobReport",
|
|
38
|
+
"resolve_login_env",
|
|
39
|
+
"cloud_server_for_env",
|
|
40
|
+
"prover_frontend_url",
|
|
41
|
+
"default_api_base_url",
|
|
42
|
+
]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.1.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Certora Ltd.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Prover Output API package.
|
|
7
|
+
|
|
8
|
+
This package contains the refactored API modules for interacting with
|
|
9
|
+
the Certora Prover output data.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .main import ProverOutputAPI
|
|
13
|
+
|
|
14
|
+
__all__ = ["ProverOutputAPI"]
|
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Certora Ltd.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
AWS Lambda-based data fetcher for CI environments.
|
|
7
|
+
Uses AWS SigV4 authentication to access Certora API through Lambda.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
from typing import Any, Dict, List, Optional, cast
|
|
12
|
+
|
|
13
|
+
import boto3
|
|
14
|
+
import requests
|
|
15
|
+
from botocore.auth import SigV4Auth
|
|
16
|
+
from botocore.awsrequest import AWSRequest
|
|
17
|
+
|
|
18
|
+
from ..exceptions import AuthenticationError, JobNotFoundError, ProverAPIError
|
|
19
|
+
from .base_data_fetcher import BaseDataFetcher
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AWSDataFetcher(BaseDataFetcher):
|
|
23
|
+
"""Handles data fetching from Certora API via AWS Lambda with SigV4 authentication."""
|
|
24
|
+
|
|
25
|
+
#: Environment variable holding the Lambda function URL (no default is baked in).
|
|
26
|
+
LAMBDA_URL_ENV_VAR = "CERTORA_LAMBDA_URL"
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self, aws_session: Optional[boto3.Session] = None, base_url: Optional[str] = None
|
|
30
|
+
):
|
|
31
|
+
"""
|
|
32
|
+
Initialize the AWS data fetcher.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
aws_session: Optional boto3 session. If not provided, creates default session.
|
|
36
|
+
base_url: Lambda function URL. If not provided, read from the
|
|
37
|
+
``CERTORA_LAMBDA_URL`` environment variable.
|
|
38
|
+
|
|
39
|
+
Raises:
|
|
40
|
+
ProverAPIError: If no base URL is provided or configured.
|
|
41
|
+
"""
|
|
42
|
+
self.session = aws_session or boto3.Session()
|
|
43
|
+
resolved = base_url or os.environ.get(self.LAMBDA_URL_ENV_VAR)
|
|
44
|
+
if not resolved:
|
|
45
|
+
raise ProverAPIError(
|
|
46
|
+
f"AWS Lambda endpoint not configured. Set the {self.LAMBDA_URL_ENV_VAR} "
|
|
47
|
+
"environment variable to the Lambda function URL, or pass base_url=."
|
|
48
|
+
)
|
|
49
|
+
self.base_url = resolved.rstrip("/")
|
|
50
|
+
self.region = "us-west-2"
|
|
51
|
+
self.service = "lambda"
|
|
52
|
+
|
|
53
|
+
def _make_signed_request(
|
|
54
|
+
self, method: str, endpoint: str, data: Optional[Dict] = None, params: Optional[Dict] = None
|
|
55
|
+
) -> requests.Response:
|
|
56
|
+
"""
|
|
57
|
+
Make a signed request to the AWS Lambda endpoint with redirect handling.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
method: HTTP method (GET, POST, etc.)
|
|
61
|
+
endpoint: Full URL endpoint
|
|
62
|
+
data: Optional JSON data for POST requests
|
|
63
|
+
params: Optional query parameters
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
requests Response object
|
|
67
|
+
|
|
68
|
+
Raises:
|
|
69
|
+
ProverAPIError: If request fails
|
|
70
|
+
"""
|
|
71
|
+
# Create AWS request for signing
|
|
72
|
+
aws_request = AWSRequest(method=method, url=endpoint, data=data, params=params)
|
|
73
|
+
|
|
74
|
+
# Sign the request with SigV4
|
|
75
|
+
credentials = self.session.get_credentials()
|
|
76
|
+
if not credentials:
|
|
77
|
+
raise AuthenticationError("No AWS credentials available. Check AWS configuration.")
|
|
78
|
+
|
|
79
|
+
SigV4Auth(credentials, self.service, self.region).add_auth(aws_request)
|
|
80
|
+
|
|
81
|
+
# Make the initial request with signed headers, but don't follow redirects
|
|
82
|
+
try:
|
|
83
|
+
response = requests.request(
|
|
84
|
+
method=method,
|
|
85
|
+
url=endpoint,
|
|
86
|
+
headers=dict(aws_request.headers),
|
|
87
|
+
json=data,
|
|
88
|
+
params=params,
|
|
89
|
+
timeout=30,
|
|
90
|
+
allow_redirects=False, # Handle redirects manually
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Handle redirects manually
|
|
94
|
+
if response.status_code in (301, 302, 303, 307, 308):
|
|
95
|
+
redirect_url = response.headers.get("Location")
|
|
96
|
+
if redirect_url:
|
|
97
|
+
# Check if redirecting to *.certora.com domain
|
|
98
|
+
from urllib.parse import urlparse
|
|
99
|
+
|
|
100
|
+
parsed_url = urlparse(redirect_url)
|
|
101
|
+
|
|
102
|
+
if parsed_url.hostname and not parsed_url.hostname.endswith(".certora.com"):
|
|
103
|
+
# For non certora.com domains, follow without AWS headers
|
|
104
|
+
response = requests.request(
|
|
105
|
+
method="GET" if response.status_code == 303 else method,
|
|
106
|
+
url=redirect_url,
|
|
107
|
+
json=data,
|
|
108
|
+
params=params,
|
|
109
|
+
timeout=30,
|
|
110
|
+
allow_redirects=True, # Allow further redirects
|
|
111
|
+
)
|
|
112
|
+
else:
|
|
113
|
+
# For certora.com domains, make request with AWS headers
|
|
114
|
+
response = requests.request(
|
|
115
|
+
method="GET" if response.status_code == 303 else method,
|
|
116
|
+
url=redirect_url,
|
|
117
|
+
headers=dict(aws_request.headers),
|
|
118
|
+
json=data,
|
|
119
|
+
params=params,
|
|
120
|
+
timeout=30,
|
|
121
|
+
allow_redirects=True,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return response
|
|
125
|
+
except requests.exceptions.RequestException as e:
|
|
126
|
+
raise ProverAPIError(f"Request failed: {e}")
|
|
127
|
+
|
|
128
|
+
def fetch_raw_output(self, job_identifier: str) -> Dict[str, Any]:
|
|
129
|
+
"""
|
|
130
|
+
Fetch raw output data from the prover API via Lambda.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
job_identifier: Job ID to fetch
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Raw API response data
|
|
137
|
+
|
|
138
|
+
Raises:
|
|
139
|
+
AuthenticationError: If authentication fails
|
|
140
|
+
JobNotFoundError: If job is not found
|
|
141
|
+
ProverAPIError: If API call fails
|
|
142
|
+
"""
|
|
143
|
+
endpoint = f"{self.base_url}/v1/domain/jobs/{job_identifier}"
|
|
144
|
+
|
|
145
|
+
try:
|
|
146
|
+
response = self._make_signed_request("GET", endpoint)
|
|
147
|
+
|
|
148
|
+
if response.status_code == 200:
|
|
149
|
+
return cast(Dict[str, Any], response.json())
|
|
150
|
+
elif response.status_code == 401:
|
|
151
|
+
raise AuthenticationError("Authentication failed - check AWS credentials")
|
|
152
|
+
elif response.status_code == 404:
|
|
153
|
+
raise JobNotFoundError(f"Job {job_identifier} not found")
|
|
154
|
+
else:
|
|
155
|
+
response.raise_for_status()
|
|
156
|
+
return cast(Dict[str, Any], response.json())
|
|
157
|
+
|
|
158
|
+
except requests.exceptions.RequestException as e:
|
|
159
|
+
raise ProverAPIError(f"Failed to fetch job data for {job_identifier}: {e}")
|
|
160
|
+
|
|
161
|
+
def fetch_tree_view_data(
|
|
162
|
+
self, job_identifier: str, path: Optional[str] = None
|
|
163
|
+
) -> Dict[str, Any]:
|
|
164
|
+
"""
|
|
165
|
+
Fetch tree-view data for a job via Lambda.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
job_identifier: Job ID to fetch
|
|
169
|
+
path: Optional path within the tree-view
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
Tree-view data
|
|
173
|
+
|
|
174
|
+
Raises:
|
|
175
|
+
AuthenticationError: If authentication fails
|
|
176
|
+
JobNotFoundError: If job is not found
|
|
177
|
+
ProverAPIError: If API call fails
|
|
178
|
+
"""
|
|
179
|
+
endpoint = f"{self.base_url}/v1/domain/jobs/{job_identifier}/tree-view"
|
|
180
|
+
|
|
181
|
+
params = {}
|
|
182
|
+
if path:
|
|
183
|
+
params["path"] = path
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
response = self._make_signed_request("GET", endpoint, params=params)
|
|
187
|
+
|
|
188
|
+
if response.status_code == 200:
|
|
189
|
+
return cast(Dict[str, Any], response.json())
|
|
190
|
+
elif response.status_code == 401:
|
|
191
|
+
raise AuthenticationError("Authentication failed - check AWS credentials")
|
|
192
|
+
elif response.status_code == 404:
|
|
193
|
+
raise JobNotFoundError(f"Tree-view data not found for job {job_identifier}")
|
|
194
|
+
else:
|
|
195
|
+
response.raise_for_status()
|
|
196
|
+
return cast(Dict[str, Any], response.json())
|
|
197
|
+
|
|
198
|
+
except requests.exceptions.RequestException as e:
|
|
199
|
+
raise ProverAPIError(f"Failed to fetch tree-view data for {job_identifier}: {e}")
|
|
200
|
+
|
|
201
|
+
def fetch_statsdata(self, job_identifier: str) -> Dict[str, Any]:
|
|
202
|
+
"""
|
|
203
|
+
Fetch statsdata.json for a job via Lambda.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
job_identifier: Job ID to fetch
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
Stats data from statsdata.json
|
|
210
|
+
|
|
211
|
+
Raises:
|
|
212
|
+
AuthenticationError: If authentication fails
|
|
213
|
+
JobNotFoundError: If job is not found
|
|
214
|
+
ProverAPIError: If API call fails
|
|
215
|
+
"""
|
|
216
|
+
endpoint = f"{self.base_url}/v1/domain/jobs/{job_identifier}/f/statsdata.json"
|
|
217
|
+
|
|
218
|
+
try:
|
|
219
|
+
response = self._make_signed_request("GET", endpoint)
|
|
220
|
+
|
|
221
|
+
if response.status_code == 200:
|
|
222
|
+
return cast(Dict[str, Any], response.json())
|
|
223
|
+
elif response.status_code == 401:
|
|
224
|
+
raise AuthenticationError("Authentication failed - check AWS credentials")
|
|
225
|
+
elif response.status_code == 404:
|
|
226
|
+
raise JobNotFoundError(f"Stats data not found for job {job_identifier}")
|
|
227
|
+
else:
|
|
228
|
+
response.raise_for_status()
|
|
229
|
+
return cast(Dict[str, Any], response.json())
|
|
230
|
+
|
|
231
|
+
except requests.exceptions.RequestException as e:
|
|
232
|
+
raise ProverAPIError(f"Failed to fetch stats data for {job_identifier}: {e}")
|
|
233
|
+
|
|
234
|
+
def fetch_outputs(self, job_identifier: str) -> bytes:
|
|
235
|
+
"""
|
|
236
|
+
Fetch outputs archive (tar.gz) for a job via Lambda.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
job_identifier: Job ID to fetch
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
Raw tar.gz content as bytes
|
|
243
|
+
|
|
244
|
+
Raises:
|
|
245
|
+
AuthenticationError: If authentication fails
|
|
246
|
+
JobNotFoundError: If job is not found
|
|
247
|
+
ProverAPIError: If API call fails
|
|
248
|
+
"""
|
|
249
|
+
endpoint = f"{self.base_url}/v1/domain/jobs/{job_identifier}/f/outputs"
|
|
250
|
+
|
|
251
|
+
try:
|
|
252
|
+
response = self._make_signed_request("GET", endpoint)
|
|
253
|
+
|
|
254
|
+
if response.status_code == 200:
|
|
255
|
+
return response.content
|
|
256
|
+
elif response.status_code == 401:
|
|
257
|
+
raise AuthenticationError("Authentication failed - check AWS credentials")
|
|
258
|
+
elif response.status_code == 404:
|
|
259
|
+
raise JobNotFoundError(f"Outputs not found for job {job_identifier}")
|
|
260
|
+
else:
|
|
261
|
+
response.raise_for_status()
|
|
262
|
+
return response.content
|
|
263
|
+
|
|
264
|
+
except requests.exceptions.RequestException as e:
|
|
265
|
+
raise ProverAPIError(f"Failed to fetch outputs for {job_identifier}: {e}")
|
|
266
|
+
|
|
267
|
+
def list_recent_jobs(
|
|
268
|
+
self,
|
|
269
|
+
created_after: str,
|
|
270
|
+
limit: int = 100,
|
|
271
|
+
all_users: bool = True,
|
|
272
|
+
) -> List[Dict[str, Any]]:
|
|
273
|
+
"""List recent jobs via Lambda.
|
|
274
|
+
|
|
275
|
+
See BaseDataFetcher.list_recent_jobs for parameter docs.
|
|
276
|
+
"""
|
|
277
|
+
endpoint = f"{self.base_url}/v1/domain/jobs"
|
|
278
|
+
|
|
279
|
+
try:
|
|
280
|
+
response = self._make_signed_request("GET", endpoint, params={
|
|
281
|
+
"currentPage": 1,
|
|
282
|
+
"pageSize": min(limit, 100),
|
|
283
|
+
"sortOrder": "desc",
|
|
284
|
+
"allUsers": "true" if all_users else "false",
|
|
285
|
+
"createdAfter": created_after,
|
|
286
|
+
"deleted": "false",
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
if response.status_code == 200:
|
|
290
|
+
return cast(List[Dict[str, Any]], response.json().get("items", []))
|
|
291
|
+
else:
|
|
292
|
+
raise ProverAPIError(
|
|
293
|
+
f"Failed to list recent jobs (status {response.status_code}): "
|
|
294
|
+
f"{response.text[:200]}"
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
except requests.exceptions.RequestException as e:
|
|
298
|
+
raise ProverAPIError(f"Failed to list recent jobs: {e}")
|
|
299
|
+
|
|
300
|
+
def fetch_group_jobs(
|
|
301
|
+
self, group_id: str, created_after: str, created_before: str
|
|
302
|
+
) -> List[Dict[str, Any]]:
|
|
303
|
+
"""Fetch all jobs in a group via Lambda."""
|
|
304
|
+
endpoint = f"{self.base_url}/v1/domain/jobs"
|
|
305
|
+
|
|
306
|
+
try:
|
|
307
|
+
response = self._make_signed_request("GET", endpoint, params={
|
|
308
|
+
"groupIds": group_id,
|
|
309
|
+
"pageSize": 200,
|
|
310
|
+
"createdAfter": created_after,
|
|
311
|
+
"createdBefore": created_before,
|
|
312
|
+
"allUsers": "true",
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
if response.status_code == 200:
|
|
316
|
+
return cast(List[Dict[str, Any]], response.json().get("items", []))
|
|
317
|
+
else:
|
|
318
|
+
raise ProverAPIError(
|
|
319
|
+
f"Failed to fetch group jobs (status {response.status_code}): "
|
|
320
|
+
f"{response.text[:200]}"
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
except requests.exceptions.RequestException as e:
|
|
324
|
+
raise ProverAPIError(f"Failed to fetch group jobs: {e}")
|
|
325
|
+
|
|
326
|
+
def cancel_jobs(self, job_ids: List[str]) -> Dict[str, Any]:
|
|
327
|
+
"""
|
|
328
|
+
Cancel multiple jobs by their IDs via Lambda.
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
job_ids: List of job IDs to cancel
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
API response data
|
|
335
|
+
|
|
336
|
+
Raises:
|
|
337
|
+
ProverAPIError: If API call fails
|
|
338
|
+
"""
|
|
339
|
+
endpoint = f"{self.base_url}/v1/domain/jobs/cancel"
|
|
340
|
+
payload = {"ids": job_ids}
|
|
341
|
+
|
|
342
|
+
try:
|
|
343
|
+
response = self._make_signed_request("POST", endpoint, data=payload)
|
|
344
|
+
|
|
345
|
+
if response.status_code == 200:
|
|
346
|
+
return cast(Dict[str, Any], response.json())
|
|
347
|
+
elif response.status_code == 401:
|
|
348
|
+
raise AuthenticationError("Authentication failed - check AWS credentials")
|
|
349
|
+
elif response.status_code == 404:
|
|
350
|
+
raise JobNotFoundError("Cancel endpoint not found")
|
|
351
|
+
else:
|
|
352
|
+
response.raise_for_status()
|
|
353
|
+
return cast(Dict[str, Any], response.json())
|
|
354
|
+
|
|
355
|
+
except requests.exceptions.RequestException as e:
|
|
356
|
+
raise ProverAPIError(f"Failed to cancel jobs {job_ids}: {e}")
|
|
357
|
+
|
|
358
|
+
def who_am_i(self) -> Dict[str, Any]:
|
|
359
|
+
"""
|
|
360
|
+
Get information about the currently authenticated user via Lambda.
|
|
361
|
+
|
|
362
|
+
Returns:
|
|
363
|
+
User information including email and user ID
|
|
364
|
+
|
|
365
|
+
Raises:
|
|
366
|
+
AuthenticationError: If authentication fails
|
|
367
|
+
ProverAPIError: If API call fails
|
|
368
|
+
"""
|
|
369
|
+
endpoint = f"{self.base_url}/who-am-i"
|
|
370
|
+
|
|
371
|
+
try:
|
|
372
|
+
response = self._make_signed_request("GET", endpoint)
|
|
373
|
+
|
|
374
|
+
if response.status_code == 200:
|
|
375
|
+
return cast(Dict[str, Any], response.json())
|
|
376
|
+
elif response.status_code == 401:
|
|
377
|
+
raise AuthenticationError("Authentication failed - check AWS credentials")
|
|
378
|
+
else:
|
|
379
|
+
response.raise_for_status()
|
|
380
|
+
return cast(Dict[str, Any], response.json())
|
|
381
|
+
|
|
382
|
+
except requests.exceptions.RequestException as e:
|
|
383
|
+
if "401" in str(e):
|
|
384
|
+
raise AuthenticationError("Authentication failed - check AWS credentials")
|
|
385
|
+
raise ProverAPIError(f"Failed to get user information: {e}")
|
|
386
|
+
|
|
387
|
+
def fetch_source_files_list(self, job_identifier: str) -> List[Dict[str, Any]]:
|
|
388
|
+
"""
|
|
389
|
+
Fetch the source file tree for a job via Lambda.
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
job_identifier: Job ID to fetch
|
|
393
|
+
|
|
394
|
+
Returns:
|
|
395
|
+
List of source file tree nodes
|
|
396
|
+
|
|
397
|
+
Raises:
|
|
398
|
+
AuthenticationError: If authentication fails
|
|
399
|
+
JobNotFoundError: If job is not found
|
|
400
|
+
ProverAPIError: If API call fails
|
|
401
|
+
"""
|
|
402
|
+
endpoint = f"{self.base_url}/v1/domain/jobs/{job_identifier}/source-files"
|
|
403
|
+
|
|
404
|
+
try:
|
|
405
|
+
response = self._make_signed_request("GET", endpoint, params={"path": ""})
|
|
406
|
+
|
|
407
|
+
if response.status_code == 200:
|
|
408
|
+
return cast(List[Dict[str, Any]], response.json())
|
|
409
|
+
elif response.status_code == 401:
|
|
410
|
+
raise AuthenticationError("Authentication failed - check AWS credentials")
|
|
411
|
+
elif response.status_code == 404:
|
|
412
|
+
raise JobNotFoundError(f"Source files not found for job {job_identifier}")
|
|
413
|
+
else:
|
|
414
|
+
response.raise_for_status()
|
|
415
|
+
return cast(List[Dict[str, Any]], response.json())
|
|
416
|
+
|
|
417
|
+
except requests.exceptions.RequestException as e:
|
|
418
|
+
raise ProverAPIError(f"Failed to fetch source files for {job_identifier}: {e}")
|
|
419
|
+
|
|
420
|
+
def fetch_source_file_content(self, job_identifier: str, path: str) -> str:
|
|
421
|
+
"""
|
|
422
|
+
Fetch the content of a specific source file for a job via Lambda.
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
job_identifier: Job ID to fetch
|
|
426
|
+
path: Path to the source file (e.g. ".certora_sources/src/Contract.sol")
|
|
427
|
+
|
|
428
|
+
Returns:
|
|
429
|
+
Raw file content as a string
|
|
430
|
+
|
|
431
|
+
Raises:
|
|
432
|
+
AuthenticationError: If authentication fails
|
|
433
|
+
JobNotFoundError: If job is not found
|
|
434
|
+
ProverAPIError: If API call fails
|
|
435
|
+
"""
|
|
436
|
+
endpoint = f"{self.base_url}/v1/domain/jobs/{job_identifier}/source-files"
|
|
437
|
+
|
|
438
|
+
try:
|
|
439
|
+
response = self._make_signed_request("GET", endpoint, params={"path": path})
|
|
440
|
+
|
|
441
|
+
if response.status_code == 200:
|
|
442
|
+
return response.text
|
|
443
|
+
elif response.status_code == 401:
|
|
444
|
+
raise AuthenticationError("Authentication failed - check AWS credentials")
|
|
445
|
+
elif response.status_code == 404:
|
|
446
|
+
raise JobNotFoundError(f"Source file not found for job {job_identifier}: {path}")
|
|
447
|
+
else:
|
|
448
|
+
response.raise_for_status()
|
|
449
|
+
return response.text
|
|
450
|
+
|
|
451
|
+
except requests.exceptions.RequestException as e:
|
|
452
|
+
raise ProverAPIError(f"Failed to fetch source file for {job_identifier}: {e}")
|
|
453
|
+
|
|
454
|
+
def fetch_alert_report(self, job_identifier: str) -> List[Dict[str, Any]]:
|
|
455
|
+
"""
|
|
456
|
+
Fetch alert report (alertReport.json) for a job via Lambda.
|
|
457
|
+
|
|
458
|
+
Args:
|
|
459
|
+
job_identifier: Job ID to fetch
|
|
460
|
+
|
|
461
|
+
Returns:
|
|
462
|
+
Alert report data as a list of alert objects
|
|
463
|
+
|
|
464
|
+
Raises:
|
|
465
|
+
AuthenticationError: If authentication fails
|
|
466
|
+
JobNotFoundError: If job is not found
|
|
467
|
+
ProverAPIError: If API call fails
|
|
468
|
+
"""
|
|
469
|
+
endpoint = f"{self.base_url}/v1/domain/jobs/{job_identifier}/f/alertReport.json"
|
|
470
|
+
|
|
471
|
+
try:
|
|
472
|
+
response = self._make_signed_request("GET", endpoint)
|
|
473
|
+
|
|
474
|
+
if response.status_code == 200:
|
|
475
|
+
return cast(List[Dict[str, Any]], response.json())
|
|
476
|
+
elif response.status_code == 401:
|
|
477
|
+
raise AuthenticationError("Authentication failed - check AWS credentials")
|
|
478
|
+
elif response.status_code == 404:
|
|
479
|
+
raise JobNotFoundError(f"Alert report not found for job {job_identifier}")
|
|
480
|
+
else:
|
|
481
|
+
response.raise_for_status()
|
|
482
|
+
return cast(List[Dict[str, Any]], response.json())
|
|
483
|
+
|
|
484
|
+
except requests.exceptions.RequestException as e:
|
|
485
|
+
raise ProverAPIError(f"Failed to fetch alert report for {job_identifier}: {e}")
|