prismor 0.1.0__py3-none-any.whl → 0.1.2__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.
- prismor/__init__.py +1 -1
- prismor/api.py +132 -8
- prismor/cli.py +186 -45
- {prismor-0.1.0.dist-info → prismor-0.1.2.dist-info}/METADATA +1 -1
- prismor-0.1.2.dist-info/RECORD +9 -0
- prismor-0.1.0.dist-info/RECORD +0 -9
- {prismor-0.1.0.dist-info → prismor-0.1.2.dist-info}/WHEEL +0 -0
- {prismor-0.1.0.dist-info → prismor-0.1.2.dist-info}/entry_points.txt +0 -0
- {prismor-0.1.0.dist-info → prismor-0.1.2.dist-info}/licenses/LICENSE +0 -0
- {prismor-0.1.0.dist-info → prismor-0.1.2.dist-info}/top_level.txt +0 -0
prismor/__init__.py
CHANGED
prismor/api.py
CHANGED
|
@@ -26,12 +26,52 @@ class PrismorClient:
|
|
|
26
26
|
"Please set it with: export PRISMOR_API_KEY=your_api_key"
|
|
27
27
|
)
|
|
28
28
|
|
|
29
|
-
self.base_url = "
|
|
29
|
+
# self.base_url = "http://localhost:3000"
|
|
30
|
+
self.base_url = "https://prismor.dev"
|
|
30
31
|
self.headers = {
|
|
31
32
|
"Authorization": f"Bearer {self.api_key}",
|
|
32
33
|
"Content-Type": "application/json"
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
def authenticate(self) -> Dict[str, Any]:
|
|
37
|
+
"""Authenticate with the Prismor API using the API key.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Dictionary containing user information and repositories
|
|
41
|
+
|
|
42
|
+
Raises:
|
|
43
|
+
PrismorAPIError: If authentication fails
|
|
44
|
+
"""
|
|
45
|
+
try:
|
|
46
|
+
response = requests.post(
|
|
47
|
+
f"{self.base_url}/api/cli/auth",
|
|
48
|
+
json={"apiKey": self.api_key},
|
|
49
|
+
headers={"Content-Type": "application/json"},
|
|
50
|
+
timeout=30
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if response.status_code == 401:
|
|
54
|
+
raise PrismorAPIError("Invalid API key. Please check your PRISMOR_API_KEY.")
|
|
55
|
+
|
|
56
|
+
if response.status_code == 400:
|
|
57
|
+
raise PrismorAPIError("API key is required.")
|
|
58
|
+
|
|
59
|
+
if response.status_code >= 400:
|
|
60
|
+
error_msg = response.json().get("error", "Authentication failed")
|
|
61
|
+
raise PrismorAPIError(f"Authentication error: {error_msg}")
|
|
62
|
+
|
|
63
|
+
response.raise_for_status()
|
|
64
|
+
return response.json()
|
|
65
|
+
|
|
66
|
+
except requests.exceptions.Timeout:
|
|
67
|
+
raise PrismorAPIError("Authentication request timed out.")
|
|
68
|
+
except requests.exceptions.ConnectionError:
|
|
69
|
+
raise PrismorAPIError(
|
|
70
|
+
"Failed to connect to Prismor API. Please check your internet connection."
|
|
71
|
+
)
|
|
72
|
+
except requests.exceptions.RequestException as e:
|
|
73
|
+
raise PrismorAPIError(f"Authentication request failed: {str(e)}")
|
|
74
|
+
|
|
35
75
|
def normalize_repo_url(self, repo: str) -> str:
|
|
36
76
|
"""Normalize repository input to a full GitHub URL.
|
|
37
77
|
|
|
@@ -59,7 +99,8 @@ class PrismorClient:
|
|
|
59
99
|
vex: bool = False,
|
|
60
100
|
sbom: bool = False,
|
|
61
101
|
detect_secret: bool = False,
|
|
62
|
-
fullscan: bool = False
|
|
102
|
+
fullscan: bool = False,
|
|
103
|
+
branch: Optional[str] = None
|
|
63
104
|
) -> Dict[str, Any]:
|
|
64
105
|
"""Perform security scan on a GitHub repository.
|
|
65
106
|
|
|
@@ -69,41 +110,59 @@ class PrismorClient:
|
|
|
69
110
|
sbom: Enable SBOM generation
|
|
70
111
|
detect_secret: Enable secret detection
|
|
71
112
|
fullscan: Enable all scan types
|
|
113
|
+
branch: Specific branch to scan (defaults to main/master)
|
|
72
114
|
|
|
73
115
|
Returns:
|
|
74
116
|
Dictionary containing scan results
|
|
75
117
|
"""
|
|
76
118
|
repo_url = self.normalize_repo_url(repo)
|
|
77
119
|
|
|
78
|
-
#
|
|
120
|
+
# First authenticate to get user info
|
|
121
|
+
auth_response = self.authenticate()
|
|
122
|
+
user_info = auth_response.get("user", {})
|
|
123
|
+
|
|
124
|
+
# Prepare request payload for CLI scan
|
|
79
125
|
payload = {
|
|
80
126
|
"repo_url": repo_url,
|
|
127
|
+
"api_key": self.api_key,
|
|
81
128
|
"vex": vex or fullscan,
|
|
82
129
|
"sbom": sbom or fullscan,
|
|
83
130
|
"detect_secret": detect_secret or fullscan,
|
|
84
|
-
"fullscan": fullscan
|
|
131
|
+
"fullscan": fullscan,
|
|
132
|
+
"branch": branch
|
|
85
133
|
}
|
|
86
134
|
|
|
87
135
|
try:
|
|
88
136
|
response = requests.post(
|
|
89
|
-
f"{self.base_url}/scan",
|
|
137
|
+
f"{self.base_url}/api/cli/scan",
|
|
90
138
|
json=payload,
|
|
91
|
-
headers=
|
|
139
|
+
headers={"Content-Type": "application/json"},
|
|
92
140
|
timeout=300 # 5 minute timeout
|
|
93
141
|
)
|
|
94
142
|
|
|
95
143
|
if response.status_code == 401:
|
|
144
|
+
error_data = response.json()
|
|
145
|
+
if error_data.get("action") == "integrate_github":
|
|
146
|
+
raise PrismorAPIError(
|
|
147
|
+
f"{error_data.get('message', 'GitHub integration required')}\n"
|
|
148
|
+
f"Please visit: {error_data.get('integration_url', 'https://prismor.dev/dashboard')}"
|
|
149
|
+
)
|
|
96
150
|
raise PrismorAPIError("Invalid API key. Please check your PRISMOR_API_KEY.")
|
|
97
151
|
|
|
98
152
|
if response.status_code == 404:
|
|
99
|
-
raise PrismorAPIError("
|
|
153
|
+
raise PrismorAPIError("CLI scan endpoint not found. Please check if CLI endpoints are available.")
|
|
100
154
|
|
|
101
155
|
if response.status_code >= 400:
|
|
102
156
|
error_msg = response.json().get("error", "Unknown error")
|
|
103
157
|
raise PrismorAPIError(f"API error: {error_msg}")
|
|
104
158
|
|
|
105
159
|
response.raise_for_status()
|
|
106
|
-
|
|
160
|
+
result = response.json()
|
|
161
|
+
|
|
162
|
+
# Handle the new response format from CLI endpoint
|
|
163
|
+
if result.get("ok") and "results" in result:
|
|
164
|
+
return result["results"]
|
|
165
|
+
return result
|
|
107
166
|
|
|
108
167
|
except requests.exceptions.Timeout:
|
|
109
168
|
raise PrismorAPIError(
|
|
@@ -115,4 +174,69 @@ class PrismorClient:
|
|
|
115
174
|
)
|
|
116
175
|
except requests.exceptions.RequestException as e:
|
|
117
176
|
raise PrismorAPIError(f"Request failed: {str(e)}")
|
|
177
|
+
|
|
178
|
+
def get_repositories(self) -> Dict[str, Any]:
|
|
179
|
+
"""Get user's repositories.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Dictionary containing user repositories
|
|
183
|
+
|
|
184
|
+
Raises:
|
|
185
|
+
PrismorAPIError: If request fails
|
|
186
|
+
"""
|
|
187
|
+
auth_response = self.authenticate()
|
|
188
|
+
user_info = auth_response.get("user", {})
|
|
189
|
+
return {
|
|
190
|
+
"repositories": user_info.get("repositories", []),
|
|
191
|
+
"user": {
|
|
192
|
+
"id": user_info.get("id"),
|
|
193
|
+
"email": user_info.get("email"),
|
|
194
|
+
"name": user_info.get("name")
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
def get_repository_by_name(self, repo_name: str) -> Dict[str, Any]:
|
|
199
|
+
"""Get repository ID by repository name.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
repo_name: Repository name (e.g., "username/repo")
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Dictionary containing repository information including ID
|
|
206
|
+
|
|
207
|
+
Raises:
|
|
208
|
+
PrismorAPIError: If request fails
|
|
209
|
+
"""
|
|
210
|
+
try:
|
|
211
|
+
response = requests.post(
|
|
212
|
+
f"{self.base_url}/api/repositories/by-name",
|
|
213
|
+
json={
|
|
214
|
+
"apiKey": self.api_key,
|
|
215
|
+
"repoName": repo_name
|
|
216
|
+
},
|
|
217
|
+
headers={"Content-Type": "application/json"},
|
|
218
|
+
timeout=30
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
if response.status_code == 401:
|
|
222
|
+
raise PrismorAPIError("Invalid API key. Please check your PRISMOR_API_KEY.")
|
|
223
|
+
|
|
224
|
+
if response.status_code == 404:
|
|
225
|
+
raise PrismorAPIError(f"Repository '{repo_name}' not found.")
|
|
226
|
+
|
|
227
|
+
if response.status_code >= 400:
|
|
228
|
+
error_msg = response.json().get("error", "Unknown error")
|
|
229
|
+
raise PrismorAPIError(f"API error: {error_msg}")
|
|
230
|
+
|
|
231
|
+
response.raise_for_status()
|
|
232
|
+
return response.json()
|
|
233
|
+
|
|
234
|
+
except requests.exceptions.Timeout:
|
|
235
|
+
raise PrismorAPIError("Request timed out.")
|
|
236
|
+
except requests.exceptions.ConnectionError:
|
|
237
|
+
raise PrismorAPIError(
|
|
238
|
+
"Failed to connect to Prismor API. Please check your internet connection."
|
|
239
|
+
)
|
|
240
|
+
except requests.exceptions.RequestException as e:
|
|
241
|
+
raise PrismorAPIError(f"Request failed: {str(e)}")
|
|
118
242
|
|
prismor/cli.py
CHANGED
|
@@ -38,56 +38,79 @@ def format_scan_results(results: dict, scan_type: str):
|
|
|
38
38
|
click.secho("Repository:", fg="yellow", bold=True)
|
|
39
39
|
click.echo(f" {results['repository']}\n")
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
click.secho(f"Status: ", fg="yellow", bold=True, nl=False)
|
|
45
|
-
click.secho(results["status"], fg=status_color)
|
|
46
|
-
click.echo()
|
|
41
|
+
if "branch" in results:
|
|
42
|
+
click.secho("Branch:", fg="yellow", bold=True)
|
|
43
|
+
click.echo(f" {results['branch']}\n")
|
|
47
44
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
if "commit_sha" in results:
|
|
46
|
+
click.secho("Commit SHA:", fg="yellow", bold=True)
|
|
47
|
+
click.echo(f" {results['commit_sha']}\n")
|
|
48
|
+
|
|
49
|
+
# Display scan results for each scan type
|
|
50
|
+
if "scans" in results:
|
|
51
|
+
scans = results["scans"]
|
|
51
52
|
|
|
52
53
|
# Vulnerability scan results
|
|
53
|
-
if "
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
if "vulnerability" in scans:
|
|
55
|
+
vuln_scan = scans["vulnerability"]
|
|
56
|
+
click.secho("Vulnerability Scan:", fg="yellow", bold=True)
|
|
57
|
+
status_color = "green" if vuln_scan.get("status") == "success" else "red"
|
|
58
|
+
click.secho(f" Status: {vuln_scan.get('status', 'unknown')}", fg=status_color)
|
|
59
|
+
|
|
60
|
+
if "scan_results" in vuln_scan:
|
|
61
|
+
scan_data = vuln_scan["scan_results"]
|
|
62
|
+
if "vulnerabilities" in scan_data or "Results" in scan_data:
|
|
63
|
+
vuln_data = scan_data.get("vulnerabilities", scan_data.get("Results", []))
|
|
64
|
+
if isinstance(vuln_data, list):
|
|
65
|
+
click.echo(f" Vulnerabilities Found: {len(vuln_data)}")
|
|
66
|
+
else:
|
|
67
|
+
click.echo(f" Vulnerabilities: Data available")
|
|
68
|
+
|
|
69
|
+
if "public_url" in vuln_scan:
|
|
70
|
+
click.echo(f" Results URL: {vuln_scan['public_url']}")
|
|
60
71
|
click.echo()
|
|
61
72
|
|
|
62
|
-
#
|
|
63
|
-
if "
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
# SBOM results
|
|
74
|
+
if "sbom" in scans:
|
|
75
|
+
sbom_scan = scans["sbom"]
|
|
76
|
+
click.secho("SBOM Generation:", fg="yellow", bold=True)
|
|
77
|
+
status_color = "green" if sbom_scan.get("status") == "success" else "red"
|
|
78
|
+
click.secho(f" Status: {sbom_scan.get('status', 'unknown')}", fg=status_color)
|
|
79
|
+
|
|
80
|
+
if "sbom" in sbom_scan:
|
|
81
|
+
sbom_data = sbom_scan["sbom"]
|
|
82
|
+
if isinstance(sbom_data, list):
|
|
83
|
+
click.echo(f" Artifacts Found: {len(sbom_data)}")
|
|
84
|
+
else:
|
|
85
|
+
click.echo(f" SBOM: Data available")
|
|
86
|
+
|
|
87
|
+
if "public_url" in sbom_scan:
|
|
88
|
+
click.echo(f" Results URL: {sbom_scan['public_url']}")
|
|
71
89
|
click.echo()
|
|
72
90
|
|
|
73
|
-
#
|
|
74
|
-
if "
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
91
|
+
# Secret scan results
|
|
92
|
+
if "secret" in scans:
|
|
93
|
+
secret_scan = scans["secret"]
|
|
94
|
+
click.secho("Secret Detection:", fg="yellow", bold=True)
|
|
95
|
+
status_color = "green" if secret_scan.get("status") == "success" else "red"
|
|
96
|
+
click.secho(f" Status: {secret_scan.get('status', 'unknown')}", fg=status_color)
|
|
97
|
+
|
|
98
|
+
if "summary" in secret_scan:
|
|
99
|
+
summary = secret_scan["summary"]
|
|
100
|
+
if isinstance(summary, dict):
|
|
101
|
+
for key, value in summary.items():
|
|
102
|
+
click.echo(f" {key}: {value}")
|
|
103
|
+
else:
|
|
104
|
+
click.echo(f" Summary: {summary}")
|
|
105
|
+
|
|
106
|
+
if "public_url" in secret_scan:
|
|
107
|
+
click.echo(f" Results URL: {secret_scan['public_url']}")
|
|
81
108
|
click.echo()
|
|
82
109
|
|
|
83
|
-
# Display
|
|
84
|
-
if "
|
|
85
|
-
click.secho("
|
|
86
|
-
click.echo(f" {results['
|
|
87
|
-
|
|
88
|
-
if "presigned_url" in results:
|
|
89
|
-
click.secho("Download URL:", fg="yellow", bold=True)
|
|
90
|
-
click.echo(f" {results['presigned_url']}\n")
|
|
110
|
+
# Display scan timestamp
|
|
111
|
+
if "scanned_at" in results:
|
|
112
|
+
click.secho("Scanned At:", fg="yellow", bold=True)
|
|
113
|
+
click.echo(f" {results['scanned_at']}\n")
|
|
91
114
|
|
|
92
115
|
click.echo("=" * 60 + "\n")
|
|
93
116
|
|
|
@@ -102,17 +125,21 @@ def format_scan_results(results: dict, scan_type: str):
|
|
|
102
125
|
@click.option("--sbom", is_flag=True, help="Generate Software Bill of Materials")
|
|
103
126
|
@click.option("--detect-secret", is_flag=True, help="Detect secrets in repository")
|
|
104
127
|
@click.option("--fullscan", is_flag=True, help="Perform all scan types")
|
|
128
|
+
@click.option("--branch", type=str, help="Specific branch to scan (defaults to main/master)")
|
|
105
129
|
@click.option("--json", "output_json", is_flag=True, help="Output results in JSON format")
|
|
106
|
-
@click.version_option(version="0.1.
|
|
130
|
+
@click.version_option(version="0.1.2", prog_name="prismor")
|
|
107
131
|
@click.pass_context
|
|
108
132
|
def cli(ctx, scan: Optional[str], vex: bool, sbom: bool, detect_secret: bool,
|
|
109
|
-
fullscan: bool, output_json: bool):
|
|
133
|
+
fullscan: bool, branch: Optional[str], output_json: bool):
|
|
110
134
|
"""Prismor CLI - Security scanning tool for GitHub repositories.
|
|
111
135
|
|
|
112
136
|
Examples:
|
|
113
137
|
prismor --scan username/repo --vex
|
|
114
138
|
prismor --scan username/repo --fullscan
|
|
115
139
|
prismor --scan https://github.com/username/repo --detect-secret
|
|
140
|
+
prismor --scan username/repo --sbom --branch develop
|
|
141
|
+
prismor status
|
|
142
|
+
prismor repos
|
|
116
143
|
"""
|
|
117
144
|
# If no command and no scan option, show help
|
|
118
145
|
if ctx.invoked_subcommand is None and not scan:
|
|
@@ -152,7 +179,8 @@ def cli(ctx, scan: Optional[str], vex: bool, sbom: bool, detect_secret: bool,
|
|
|
152
179
|
vex=vex,
|
|
153
180
|
sbom=sbom,
|
|
154
181
|
detect_secret=detect_secret,
|
|
155
|
-
fullscan=fullscan
|
|
182
|
+
fullscan=fullscan,
|
|
183
|
+
branch=branch
|
|
156
184
|
)
|
|
157
185
|
|
|
158
186
|
# Output results
|
|
@@ -161,6 +189,41 @@ def cli(ctx, scan: Optional[str], vex: bool, sbom: bool, detect_secret: bool,
|
|
|
161
189
|
else:
|
|
162
190
|
print_success("Scan completed successfully!")
|
|
163
191
|
format_scan_results(results, ', '.join(scan_types))
|
|
192
|
+
|
|
193
|
+
# Try to get repository ID and display dashboard link
|
|
194
|
+
try:
|
|
195
|
+
# Extract repo name from scan input
|
|
196
|
+
repo_name = scan
|
|
197
|
+
if scan.startswith("http://") or scan.startswith("https://"):
|
|
198
|
+
# Extract from GitHub URL
|
|
199
|
+
if "github.com/" in scan:
|
|
200
|
+
repo_name = scan.split("github.com/")[1].rstrip("/")
|
|
201
|
+
|
|
202
|
+
# Get repository ID
|
|
203
|
+
repo_info = client.get_repository_by_name(repo_name)
|
|
204
|
+
if repo_info.get("success") and "repository" in repo_info:
|
|
205
|
+
repo_id = repo_info["repository"]["id"]
|
|
206
|
+
dashboard_url = f"https://prismor.dev/repositories/{repo_id}"
|
|
207
|
+
|
|
208
|
+
click.echo("\n" + "=" * 60)
|
|
209
|
+
click.secho(" 📊 Dashboard Analysis", fg="cyan", bold=True)
|
|
210
|
+
click.echo("=" * 60)
|
|
211
|
+
click.secho(f"🔗 View detailed analysis and insights:", fg="blue")
|
|
212
|
+
click.secho(f" {dashboard_url}", fg="green", bold=True)
|
|
213
|
+
click.echo("\n💡 The dashboard provides:")
|
|
214
|
+
click.echo(" • Interactive visualizations and charts")
|
|
215
|
+
click.echo(" • Historical vulnerability trends")
|
|
216
|
+
click.echo(" • Detailed security reports")
|
|
217
|
+
click.echo(" • Team collaboration features")
|
|
218
|
+
click.echo(" • Export capabilities")
|
|
219
|
+
click.echo("=" * 60 + "\n")
|
|
220
|
+
|
|
221
|
+
except PrismorAPIError as e:
|
|
222
|
+
# Repository might not be found, continue without dashboard link
|
|
223
|
+
print_warning(f"Could not generate dashboard link: {str(e)}")
|
|
224
|
+
except Exception as e:
|
|
225
|
+
# Any other error, continue without dashboard link
|
|
226
|
+
print_warning(f"Could not generate dashboard link: {str(e)}")
|
|
164
227
|
|
|
165
228
|
except PrismorAPIError as e:
|
|
166
229
|
print_error(str(e))
|
|
@@ -196,10 +259,88 @@ def config():
|
|
|
196
259
|
click.echo("\nTo set your API key, run:")
|
|
197
260
|
click.echo(" export PRISMOR_API_KEY=your_api_key")
|
|
198
261
|
|
|
199
|
-
click.echo("\nAPI Endpoint:
|
|
262
|
+
# click.echo("\nAPI Endpoint: http://localhost:3000")
|
|
200
263
|
click.echo("=" * 60 + "\n")
|
|
201
264
|
|
|
202
265
|
|
|
266
|
+
@cli.command()
|
|
267
|
+
def repos():
|
|
268
|
+
"""List your connected repositories."""
|
|
269
|
+
try:
|
|
270
|
+
client = PrismorClient()
|
|
271
|
+
repos_data = client.get_repositories()
|
|
272
|
+
|
|
273
|
+
click.echo("\n" + "=" * 60)
|
|
274
|
+
click.secho(" Your Repositories", fg="cyan", bold=True)
|
|
275
|
+
click.echo("=" * 60 + "\n")
|
|
276
|
+
|
|
277
|
+
user_info = repos_data.get("user", {})
|
|
278
|
+
if user_info:
|
|
279
|
+
click.secho(f"User: {user_info.get('name', 'Unknown')} ({user_info.get('email', 'No email')})", fg="yellow")
|
|
280
|
+
click.echo()
|
|
281
|
+
|
|
282
|
+
repositories = repos_data.get("repositories", [])
|
|
283
|
+
if repositories:
|
|
284
|
+
for repo in repositories:
|
|
285
|
+
click.secho(f"• {repo.get('name', 'Unknown')}", fg="green")
|
|
286
|
+
click.echo(f" URL: {repo.get('htmlUrl', 'No URL')}")
|
|
287
|
+
click.echo(f" Owner: {repo.get('githubOwner', 'Unknown')}")
|
|
288
|
+
click.echo()
|
|
289
|
+
else:
|
|
290
|
+
print_warning("No repositories found. Connect repositories through the web interface.")
|
|
291
|
+
|
|
292
|
+
click.echo("=" * 60 + "\n")
|
|
293
|
+
|
|
294
|
+
except PrismorAPIError as e:
|
|
295
|
+
print_error(str(e))
|
|
296
|
+
sys.exit(1)
|
|
297
|
+
except Exception as e:
|
|
298
|
+
print_error(f"Unexpected error: {str(e)}")
|
|
299
|
+
sys.exit(1)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
@cli.command()
|
|
303
|
+
def status():
|
|
304
|
+
"""Check your account status and GitHub integration."""
|
|
305
|
+
try:
|
|
306
|
+
client = PrismorClient()
|
|
307
|
+
auth_data = client.authenticate()
|
|
308
|
+
|
|
309
|
+
click.echo("\n" + "=" * 60)
|
|
310
|
+
click.secho(" Account Status", fg="cyan", bold=True)
|
|
311
|
+
click.echo("=" * 60 + "\n")
|
|
312
|
+
|
|
313
|
+
user_info = auth_data.get("user", {})
|
|
314
|
+
if user_info:
|
|
315
|
+
click.secho(f"User: {user_info.get('name', 'Unknown')} ({user_info.get('email', 'No email')})", fg="yellow")
|
|
316
|
+
click.echo()
|
|
317
|
+
|
|
318
|
+
repositories = user_info.get("repositories", [])
|
|
319
|
+
click.secho(f"Connected Repositories: {len(repositories)}", fg="green")
|
|
320
|
+
|
|
321
|
+
if repositories:
|
|
322
|
+
click.echo("\nRepository List:")
|
|
323
|
+
for repo in repositories:
|
|
324
|
+
click.echo(f" • {repo.get('name', 'Unknown')} ({repo.get('htmlUrl', 'No URL')})")
|
|
325
|
+
else:
|
|
326
|
+
print_warning("No repositories connected.")
|
|
327
|
+
click.echo("\nTo connect repositories:")
|
|
328
|
+
click.echo(" 1. Visit https://prismor.dev/dashboard")
|
|
329
|
+
click.echo(" 2. Connect your GitHub account")
|
|
330
|
+
click.echo(" 3. Select repositories to scan")
|
|
331
|
+
else:
|
|
332
|
+
print_error("Failed to retrieve account information.")
|
|
333
|
+
|
|
334
|
+
click.echo("\n" + "=" * 60 + "\n")
|
|
335
|
+
|
|
336
|
+
except PrismorAPIError as e:
|
|
337
|
+
print_error(str(e))
|
|
338
|
+
sys.exit(1)
|
|
339
|
+
except Exception as e:
|
|
340
|
+
print_error(f"Unexpected error: {str(e)}")
|
|
341
|
+
sys.exit(1)
|
|
342
|
+
|
|
343
|
+
|
|
203
344
|
def main():
|
|
204
345
|
"""Entry point for the CLI."""
|
|
205
346
|
cli()
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
prismor/__init__.py,sha256=6sRGygr6VrNie8Xd_B9Zeq6Q0ThWRftLBnVzZBdGEb4,230
|
|
2
|
+
prismor/api.py,sha256=YrFnw1adT4ci6ehR4qB03kSOxw9l1o4fm0KSGUlDU4s,8886
|
|
3
|
+
prismor/cli.py,sha256=K0aOxtbhE-gUoRw7selqT1a7BTr80A4Ogvvcspx5BUk,13582
|
|
4
|
+
prismor-0.1.2.dist-info/licenses/LICENSE,sha256=qWFF8Eh6gpZOq_3effdd6hfeMN2WN9ZG4vOyFk2MyhU,1065
|
|
5
|
+
prismor-0.1.2.dist-info/METADATA,sha256=sDwR9KP4wlNOdQON-j-42Sabowz2q7IhCXb4dss8s1I,9394
|
|
6
|
+
prismor-0.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
+
prismor-0.1.2.dist-info/entry_points.txt,sha256=Uiu0HW04eq2Gb6sQC9o-LqMKMyW1SKwkojxrkFeVfqg,45
|
|
8
|
+
prismor-0.1.2.dist-info/top_level.txt,sha256=nlJGoJ3fQXRL27RXQ5LJU2LX1kl1VSgKXyKjcSR28lw,8
|
|
9
|
+
prismor-0.1.2.dist-info/RECORD,,
|
prismor-0.1.0.dist-info/RECORD
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
prismor/__init__.py,sha256=d_7a1UaXHGT7kAzI5fERetpR2vEj349becGSTKatQU0,230
|
|
2
|
-
prismor/api.py,sha256=jnbxgzDJjYLGxC5viT18xVjcmbupFecvlb-ph8J0yik,3938
|
|
3
|
-
prismor/cli.py,sha256=zjRJH9li_0_2CNquI4Y14alGpmiq1d-P_rZBTp4bS6I,7310
|
|
4
|
-
prismor-0.1.0.dist-info/licenses/LICENSE,sha256=qWFF8Eh6gpZOq_3effdd6hfeMN2WN9ZG4vOyFk2MyhU,1065
|
|
5
|
-
prismor-0.1.0.dist-info/METADATA,sha256=2boiqdtLo65Do3pxw_P7wWmU-FjVv95S43b3xfNT7Ps,9394
|
|
6
|
-
prismor-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
-
prismor-0.1.0.dist-info/entry_points.txt,sha256=Uiu0HW04eq2Gb6sQC9o-LqMKMyW1SKwkojxrkFeVfqg,45
|
|
8
|
-
prismor-0.1.0.dist-info/top_level.txt,sha256=nlJGoJ3fQXRL27RXQ5LJU2LX1kl1VSgKXyKjcSR28lw,8
|
|
9
|
-
prismor-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|