codeshield-ai 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.
- codeshield/__init__.py +62 -0
- codeshield/api_server.py +438 -0
- codeshield/cli.py +48 -0
- codeshield/contextvault/__init__.py +1 -0
- codeshield/contextvault/capture.py +174 -0
- codeshield/contextvault/restore.py +115 -0
- codeshield/mcp/__init__.py +1 -0
- codeshield/mcp/hooks.py +65 -0
- codeshield/mcp/server.py +319 -0
- codeshield/styleforge/__init__.py +1 -0
- codeshield/styleforge/corrector.py +298 -0
- codeshield/trustgate/__init__.py +1 -0
- codeshield/trustgate/checker.py +384 -0
- codeshield/trustgate/sandbox.py +101 -0
- codeshield/utils/__init__.py +9 -0
- codeshield/utils/daytona.py +233 -0
- codeshield/utils/leanmcp.py +258 -0
- codeshield/utils/llm.py +423 -0
- codeshield/utils/metrics.py +543 -0
- codeshield/utils/token_optimizer.py +605 -0
- codeshield_ai-0.1.0.dist-info/METADATA +565 -0
- codeshield_ai-0.1.0.dist-info/RECORD +24 -0
- codeshield_ai-0.1.0.dist-info/WHEEL +4 -0
- codeshield_ai-0.1.0.dist-info/entry_points.txt +3 -0
codeshield/__init__.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CodeShield - The Complete AI Coding Safety Net
|
|
3
|
+
|
|
4
|
+
Verify, Enforce, Remember - AI-powered code verification for safe development.
|
|
5
|
+
|
|
6
|
+
Example usage:
|
|
7
|
+
>>> from codeshield import verify_code, check_style, full_verify
|
|
8
|
+
>>>
|
|
9
|
+
>>> # Quick verification
|
|
10
|
+
>>> result = verify_code("print('hello')")
|
|
11
|
+
>>> print(result.is_valid)
|
|
12
|
+
True
|
|
13
|
+
>>>
|
|
14
|
+
>>> # Full verification with sandbox execution
|
|
15
|
+
>>> result = full_verify("x = 1 + 2\\nprint(x)")
|
|
16
|
+
>>> print(result['overall_valid'])
|
|
17
|
+
True
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
__version__ = "0.1.0"
|
|
21
|
+
__author__ = "CodeShield Team"
|
|
22
|
+
__license__ = "MIT"
|
|
23
|
+
|
|
24
|
+
# Core verification functions
|
|
25
|
+
from codeshield.trustgate.checker import verify_code, VerificationResult
|
|
26
|
+
from codeshield.trustgate.sandbox import full_verification as full_verify, SandboxVerification
|
|
27
|
+
|
|
28
|
+
# Style checking
|
|
29
|
+
from codeshield.styleforge.corrector import check_style, StyleCheckResult
|
|
30
|
+
|
|
31
|
+
# Context management
|
|
32
|
+
from codeshield.contextvault.capture import save_context, list_contexts, get_context
|
|
33
|
+
from codeshield.contextvault.restore import restore_context
|
|
34
|
+
|
|
35
|
+
# Utilities
|
|
36
|
+
from codeshield.utils.daytona import DaytonaClient, get_daytona_client
|
|
37
|
+
from codeshield.utils.llm import LLMClient, get_llm_client
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
# Version info
|
|
41
|
+
"__version__",
|
|
42
|
+
"__author__",
|
|
43
|
+
"__license__",
|
|
44
|
+
# Core verification
|
|
45
|
+
"verify_code",
|
|
46
|
+
"full_verify",
|
|
47
|
+
"VerificationResult",
|
|
48
|
+
"SandboxVerification",
|
|
49
|
+
# Style
|
|
50
|
+
"check_style",
|
|
51
|
+
"StyleCheckResult",
|
|
52
|
+
# Context
|
|
53
|
+
"save_context",
|
|
54
|
+
"restore_context",
|
|
55
|
+
"list_contexts",
|
|
56
|
+
"get_context",
|
|
57
|
+
# Utilities
|
|
58
|
+
"DaytonaClient",
|
|
59
|
+
"get_daytona_client",
|
|
60
|
+
"LLMClient",
|
|
61
|
+
"get_llm_client",
|
|
62
|
+
]
|
codeshield/api_server.py
ADDED
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CodeShield API Server
|
|
3
|
+
Exposes CodeShield functionality via HTTP for the React Frontend.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from fastapi import FastAPI, HTTPException, Body
|
|
7
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
from typing import List, Optional, Any
|
|
10
|
+
import uvicorn
|
|
11
|
+
import os
|
|
12
|
+
from fastapi.staticfiles import StaticFiles
|
|
13
|
+
from fastapi.responses import FileResponse
|
|
14
|
+
|
|
15
|
+
# Import CodeShield core modules
|
|
16
|
+
# We wrap imports in try/except to handle potential missing dependencies during dev
|
|
17
|
+
try:
|
|
18
|
+
from codeshield.trustgate.checker import verify_code
|
|
19
|
+
from codeshield.trustgate.sandbox import full_verification
|
|
20
|
+
from codeshield.styleforge.corrector import check_style
|
|
21
|
+
from codeshield.contextvault.capture import save_context, list_contexts
|
|
22
|
+
from codeshield.contextvault.restore import restore_context
|
|
23
|
+
except ImportError:
|
|
24
|
+
# Fallback for dev environment if paths aren't set up
|
|
25
|
+
print("WARNING: Could not import CodeShield modules. Ensure you are running as a module.")
|
|
26
|
+
verify_code = None
|
|
27
|
+
full_verification = None
|
|
28
|
+
check_style = None
|
|
29
|
+
save_context = None
|
|
30
|
+
list_contexts = None
|
|
31
|
+
restore_context = None
|
|
32
|
+
|
|
33
|
+
app = FastAPI(title="CodeShield API", version="0.1.0")
|
|
34
|
+
|
|
35
|
+
# Configure CORS
|
|
36
|
+
app.add_middleware(
|
|
37
|
+
CORSMiddleware,
|
|
38
|
+
allow_origins=["http://localhost:5173", "http://127.0.0.1:5173"], # Vite dev server
|
|
39
|
+
allow_credentials=True,
|
|
40
|
+
allow_methods=["*"],
|
|
41
|
+
allow_headers=["*"],
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# --- Data Models ---
|
|
45
|
+
|
|
46
|
+
class VerifyRequest(BaseModel):
|
|
47
|
+
code: str
|
|
48
|
+
auto_fix: bool = True
|
|
49
|
+
use_sandbox: bool = False
|
|
50
|
+
|
|
51
|
+
class StyleCheckRequest(BaseModel):
|
|
52
|
+
code: str
|
|
53
|
+
codebase_path: str = "."
|
|
54
|
+
|
|
55
|
+
class ContextSaveRequest(BaseModel):
|
|
56
|
+
name: str
|
|
57
|
+
files: List[str] = []
|
|
58
|
+
cursor: Optional[dict] = None
|
|
59
|
+
notes: Optional[str] = None
|
|
60
|
+
|
|
61
|
+
class ContextRestoreRequest(BaseModel):
|
|
62
|
+
name: str
|
|
63
|
+
|
|
64
|
+
# --- Endpoints ---
|
|
65
|
+
|
|
66
|
+
@app.get("/api/health")
|
|
67
|
+
async def health_check():
|
|
68
|
+
return {"status": "online", "service": "CodeShield API"}
|
|
69
|
+
|
|
70
|
+
@app.post("/api/verify")
|
|
71
|
+
async def api_verify_code(req: VerifyRequest):
|
|
72
|
+
if not verify_code:
|
|
73
|
+
return {"error": "Backend modules not loaded"}
|
|
74
|
+
|
|
75
|
+
if req.use_sandbox:
|
|
76
|
+
# Full sandbox verification
|
|
77
|
+
result = full_verification(req.code)
|
|
78
|
+
return result
|
|
79
|
+
else:
|
|
80
|
+
# Fast static verification
|
|
81
|
+
result = verify_code(req.code, auto_fix=req.auto_fix)
|
|
82
|
+
return result.to_dict()
|
|
83
|
+
|
|
84
|
+
@app.post("/api/style")
|
|
85
|
+
async def api_check_style(req: StyleCheckRequest):
|
|
86
|
+
if not check_style:
|
|
87
|
+
return {"error": "Backend modules not loaded"}
|
|
88
|
+
|
|
89
|
+
result = check_style(req.code, req.codebase_path)
|
|
90
|
+
return result.to_dict()
|
|
91
|
+
|
|
92
|
+
@app.post("/api/context/save")
|
|
93
|
+
async def api_save_context(req: ContextSaveRequest):
|
|
94
|
+
if not save_context:
|
|
95
|
+
return {"error": "Backend modules not loaded"}
|
|
96
|
+
|
|
97
|
+
result = save_context(
|
|
98
|
+
name=req.name,
|
|
99
|
+
files=req.files,
|
|
100
|
+
cursor=req.cursor,
|
|
101
|
+
notes=req.notes
|
|
102
|
+
)
|
|
103
|
+
return result
|
|
104
|
+
|
|
105
|
+
@app.post("/api/context/restore")
|
|
106
|
+
async def api_restore_context(req: ContextRestoreRequest):
|
|
107
|
+
if not restore_context:
|
|
108
|
+
return {"error": "Backend modules not loaded"}
|
|
109
|
+
|
|
110
|
+
result = restore_context(name=req.name)
|
|
111
|
+
return result
|
|
112
|
+
|
|
113
|
+
@app.get("/api/contexts")
|
|
114
|
+
async def api_list_contexts():
|
|
115
|
+
if not list_contexts:
|
|
116
|
+
return {"error": "Backend modules not loaded"}
|
|
117
|
+
|
|
118
|
+
result = list_contexts()
|
|
119
|
+
return result
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# --- Observability Endpoints ---
|
|
123
|
+
|
|
124
|
+
@app.get("/api/providers/status")
|
|
125
|
+
async def api_provider_status():
|
|
126
|
+
"""
|
|
127
|
+
Get status of all LLM providers (CometAPI, Novita, AIML).
|
|
128
|
+
Useful for checking which providers are configured and their usage stats.
|
|
129
|
+
"""
|
|
130
|
+
try:
|
|
131
|
+
from codeshield.utils.llm import get_llm_client, get_provider_stats
|
|
132
|
+
|
|
133
|
+
llm = get_llm_client()
|
|
134
|
+
status = llm.get_status()
|
|
135
|
+
stats = get_provider_stats()
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
"providers": status,
|
|
139
|
+
"usage_stats": stats,
|
|
140
|
+
"active_provider": next(
|
|
141
|
+
(name for name, info in status.items() if info["configured"]),
|
|
142
|
+
None
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
except Exception as e:
|
|
146
|
+
return {"error": str(e)}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@app.get("/api/providers/test")
|
|
150
|
+
async def api_test_provider(provider: str = None):
|
|
151
|
+
"""
|
|
152
|
+
Test LLM provider connectivity.
|
|
153
|
+
Query param: ?provider=cometapi|novita|aiml
|
|
154
|
+
"""
|
|
155
|
+
try:
|
|
156
|
+
import time
|
|
157
|
+
from codeshield.utils.llm import get_llm_client
|
|
158
|
+
|
|
159
|
+
llm = get_llm_client()
|
|
160
|
+
if provider:
|
|
161
|
+
llm.preferred_provider = provider
|
|
162
|
+
|
|
163
|
+
start_time = time.time()
|
|
164
|
+
response = llm.chat(
|
|
165
|
+
prompt="Reply with exactly: 'CodeShield connected'",
|
|
166
|
+
max_tokens=20
|
|
167
|
+
)
|
|
168
|
+
elapsed = time.time() - start_time
|
|
169
|
+
|
|
170
|
+
if response:
|
|
171
|
+
return {
|
|
172
|
+
"success": True,
|
|
173
|
+
"provider": response.provider,
|
|
174
|
+
"model": response.model,
|
|
175
|
+
"response": response.content,
|
|
176
|
+
"response_time_ms": round(elapsed * 1000),
|
|
177
|
+
"tokens_used": response.tokens_used
|
|
178
|
+
}
|
|
179
|
+
else:
|
|
180
|
+
return {
|
|
181
|
+
"success": False,
|
|
182
|
+
"error": "No LLM provider available",
|
|
183
|
+
"hint": "Set COMETAPI_KEY, NOVITA_API_KEY, or AIML_API_KEY in .env"
|
|
184
|
+
}
|
|
185
|
+
except Exception as e:
|
|
186
|
+
return {"success": False, "error": str(e)}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@app.get("/api/mcp/status")
|
|
190
|
+
async def api_mcp_status():
|
|
191
|
+
"""
|
|
192
|
+
Check if MCP server components are available.
|
|
193
|
+
"""
|
|
194
|
+
try:
|
|
195
|
+
from mcp.server.fastmcp import FastMCP
|
|
196
|
+
mcp_available = True
|
|
197
|
+
except ImportError:
|
|
198
|
+
mcp_available = False
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
"mcp_sdk_installed": mcp_available,
|
|
202
|
+
"mcp_config": "mcp_config.json",
|
|
203
|
+
"tools_available": [
|
|
204
|
+
"verify_code", "full_verify", "check_style",
|
|
205
|
+
"save_context", "restore_context", "list_contexts",
|
|
206
|
+
"mcp_health", "test_llm_connection"
|
|
207
|
+
] if mcp_available else [],
|
|
208
|
+
"usage": "Add mcp_config.json to your Claude/Cursor MCP settings"
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@app.get("/api/leanmcp/status")
|
|
213
|
+
async def api_leanmcp_status():
|
|
214
|
+
"""
|
|
215
|
+
Get LeanMCP integration status and metrics.
|
|
216
|
+
"""
|
|
217
|
+
try:
|
|
218
|
+
from codeshield.utils.leanmcp import get_leanmcp_client
|
|
219
|
+
|
|
220
|
+
client = get_leanmcp_client()
|
|
221
|
+
return {
|
|
222
|
+
"status": client.get_status(),
|
|
223
|
+
"metrics": client.get_metrics(),
|
|
224
|
+
"configured": client.is_configured()
|
|
225
|
+
}
|
|
226
|
+
except Exception as e:
|
|
227
|
+
return {"error": str(e), "configured": False}
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
@app.get("/api/leanmcp/health")
|
|
231
|
+
async def api_leanmcp_health():
|
|
232
|
+
"""
|
|
233
|
+
Report and retrieve health status via LeanMCP.
|
|
234
|
+
"""
|
|
235
|
+
try:
|
|
236
|
+
from codeshield.utils.leanmcp import get_leanmcp_client
|
|
237
|
+
|
|
238
|
+
client = get_leanmcp_client()
|
|
239
|
+
return client.report_health()
|
|
240
|
+
except Exception as e:
|
|
241
|
+
return {"error": str(e), "status": "error"}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@app.get("/api/integrations/status")
|
|
245
|
+
async def api_integrations_status():
|
|
246
|
+
"""
|
|
247
|
+
Get status of ALL required integrations.
|
|
248
|
+
"""
|
|
249
|
+
import os
|
|
250
|
+
|
|
251
|
+
integrations = {
|
|
252
|
+
"cometapi": {
|
|
253
|
+
"configured": bool(os.getenv("COMETAPI_KEY")),
|
|
254
|
+
"env_var": "COMETAPI_KEY",
|
|
255
|
+
"docs": "https://apidoc.cometapi.com/"
|
|
256
|
+
},
|
|
257
|
+
"novita": {
|
|
258
|
+
"configured": bool(os.getenv("NOVITA_API_KEY")),
|
|
259
|
+
"env_var": "NOVITA_API_KEY",
|
|
260
|
+
"docs": "https://novita.ai/docs/guides/llm-api"
|
|
261
|
+
},
|
|
262
|
+
"aiml": {
|
|
263
|
+
"configured": bool(os.getenv("AIML_API_KEY")),
|
|
264
|
+
"env_var": "AIML_API_KEY",
|
|
265
|
+
},
|
|
266
|
+
"daytona": {
|
|
267
|
+
"configured": bool(os.getenv("DAYTONA_API_KEY")),
|
|
268
|
+
"env_var": "DAYTONA_API_KEY",
|
|
269
|
+
"api_url": os.getenv("DAYTONA_API_URL"),
|
|
270
|
+
"docs": "https://www.daytona.io/docs"
|
|
271
|
+
},
|
|
272
|
+
"leanmcp": {
|
|
273
|
+
"configured": bool(os.getenv("LEANMCP_KEY")),
|
|
274
|
+
"env_var": "LEANMCP_KEY",
|
|
275
|
+
"api_url": os.getenv("LEANMCP_API_URL"),
|
|
276
|
+
"docs": "https://docs.leanmcp.com/"
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
all_configured = all(i["configured"] for i in integrations.values())
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
"all_configured": all_configured,
|
|
284
|
+
"integrations": integrations,
|
|
285
|
+
"message": "All integrations configured!" if all_configured else "Some integrations missing. Check .env file."
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
# --- Metrics Endpoints ---
|
|
290
|
+
|
|
291
|
+
@app.get("/api/metrics")
|
|
292
|
+
async def api_get_metrics():
|
|
293
|
+
"""
|
|
294
|
+
Get comprehensive CodeShield metrics.
|
|
295
|
+
|
|
296
|
+
Returns transparent, verifiable statistics for:
|
|
297
|
+
- TrustGate: Detection rates, fix accuracy, sandbox success
|
|
298
|
+
- StyleForge: Convention detection, corrections
|
|
299
|
+
- ContextVault: Save/restore stats
|
|
300
|
+
- Tokens: Usage efficiency, costs
|
|
301
|
+
"""
|
|
302
|
+
try:
|
|
303
|
+
from codeshield.utils.metrics import get_metrics
|
|
304
|
+
|
|
305
|
+
metrics = get_metrics()
|
|
306
|
+
return metrics.get_summary()
|
|
307
|
+
except Exception as e:
|
|
308
|
+
return {"error": str(e)}
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
@app.get("/api/metrics/trustgate")
|
|
312
|
+
async def api_trustgate_metrics():
|
|
313
|
+
"""Get TrustGate-specific metrics"""
|
|
314
|
+
try:
|
|
315
|
+
from codeshield.utils.metrics import get_metrics
|
|
316
|
+
return get_metrics().trustgate.to_dict()
|
|
317
|
+
except Exception as e:
|
|
318
|
+
return {"error": str(e)}
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
@app.get("/api/metrics/styleforge")
|
|
322
|
+
async def api_styleforge_metrics():
|
|
323
|
+
"""Get StyleForge-specific metrics"""
|
|
324
|
+
try:
|
|
325
|
+
from codeshield.utils.metrics import get_metrics
|
|
326
|
+
return get_metrics().styleforge.to_dict()
|
|
327
|
+
except Exception as e:
|
|
328
|
+
return {"error": str(e)}
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
@app.get("/api/metrics/tokens")
|
|
332
|
+
async def api_token_metrics():
|
|
333
|
+
"""
|
|
334
|
+
Get token utilization metrics.
|
|
335
|
+
|
|
336
|
+
Includes:
|
|
337
|
+
- Total tokens used (input/output)
|
|
338
|
+
- Token efficiency ratio
|
|
339
|
+
- Cost estimates per provider
|
|
340
|
+
- Average tokens per request
|
|
341
|
+
"""
|
|
342
|
+
try:
|
|
343
|
+
from codeshield.utils.metrics import get_metrics
|
|
344
|
+
return get_metrics().tokens.to_dict()
|
|
345
|
+
except Exception as e:
|
|
346
|
+
return {"error": str(e)}
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
@app.post("/api/metrics/reset")
|
|
350
|
+
async def api_reset_metrics():
|
|
351
|
+
"""Reset all metrics (for testing)"""
|
|
352
|
+
try:
|
|
353
|
+
from codeshield.utils.metrics import get_metrics
|
|
354
|
+
get_metrics().reset()
|
|
355
|
+
return {"success": True, "message": "Metrics reset"}
|
|
356
|
+
except Exception as e:
|
|
357
|
+
return {"error": str(e)}
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
@app.get("/api/tokens/efficiency")
|
|
361
|
+
async def api_token_efficiency():
|
|
362
|
+
"""
|
|
363
|
+
Get token efficiency and optimization stats.
|
|
364
|
+
|
|
365
|
+
Shows:
|
|
366
|
+
- Cache hit rate (higher = more savings)
|
|
367
|
+
- Tokens saved by caching
|
|
368
|
+
- Budget usage
|
|
369
|
+
- Session statistics
|
|
370
|
+
"""
|
|
371
|
+
try:
|
|
372
|
+
from codeshield.utils.token_optimizer import get_token_optimizer
|
|
373
|
+
from codeshield.utils.llm import get_provider_stats
|
|
374
|
+
|
|
375
|
+
optimizer = get_token_optimizer()
|
|
376
|
+
provider_stats = get_provider_stats()
|
|
377
|
+
|
|
378
|
+
return {
|
|
379
|
+
"optimization": optimizer.get_stats(),
|
|
380
|
+
"provider_efficiency": {
|
|
381
|
+
name: {
|
|
382
|
+
"token_efficiency": stats.get("token_efficiency", 0),
|
|
383
|
+
"avg_tokens_per_call": stats.get("avg_tokens_per_call", 0),
|
|
384
|
+
"avg_latency_ms": stats.get("avg_latency_ms", 0),
|
|
385
|
+
}
|
|
386
|
+
for name, stats in provider_stats.items()
|
|
387
|
+
},
|
|
388
|
+
"tips": [
|
|
389
|
+
"Cache hit rate > 20% indicates good prompt reuse",
|
|
390
|
+
"Token efficiency > 1.0 means more output than input (verbose responses)",
|
|
391
|
+
"Aim for avg_tokens_per_call < 500 for cost efficiency"
|
|
392
|
+
]
|
|
393
|
+
}
|
|
394
|
+
except Exception as e:
|
|
395
|
+
return {"error": str(e)}
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
@app.post("/api/tokens/budget")
|
|
399
|
+
async def api_set_token_budget(budget: int = 100000):
|
|
400
|
+
"""Set token budget for the session"""
|
|
401
|
+
try:
|
|
402
|
+
from codeshield.utils.token_optimizer import get_token_optimizer
|
|
403
|
+
optimizer = get_token_optimizer()
|
|
404
|
+
optimizer.set_budget(budget)
|
|
405
|
+
return {"success": True, "budget": budget}
|
|
406
|
+
except Exception as e:
|
|
407
|
+
return {"error": str(e)}
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
# --- Frontend Static Serving ---
|
|
411
|
+
|
|
412
|
+
# Mount assets (CSS/JS)
|
|
413
|
+
if os.path.exists("frontend/dist/assets"):
|
|
414
|
+
app.mount("/assets", StaticFiles(directory="frontend/dist/assets"), name="assets")
|
|
415
|
+
|
|
416
|
+
# Serve other static files (favicon, etc.) and SPA fallback
|
|
417
|
+
@app.get("/{full_path:path}")
|
|
418
|
+
async def serve_static(full_path: str):
|
|
419
|
+
# API requests are handled by routes above.
|
|
420
|
+
# If we get here, it's a static file or client-side route.
|
|
421
|
+
|
|
422
|
+
# Security: Prevent traversing up? FileResponse handles it.
|
|
423
|
+
|
|
424
|
+
dist_dir = "frontend/dist"
|
|
425
|
+
if not os.path.exists(dist_dir):
|
|
426
|
+
return {"status": "Frontend not built", "deployment": "backend-only"}
|
|
427
|
+
|
|
428
|
+
# Try to find specific file
|
|
429
|
+
file_path = os.path.join(dist_dir, full_path)
|
|
430
|
+
if os.path.exists(file_path) and os.path.isfile(file_path):
|
|
431
|
+
return FileResponse(file_path)
|
|
432
|
+
|
|
433
|
+
# Fallback to index.html for SPA routing
|
|
434
|
+
return FileResponse(os.path.join(dist_dir, "index.html"))
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
if __name__ == "__main__":
|
|
438
|
+
uvicorn.run("codeshield.api_server:app", host="0.0.0.0", port=8000, reload=True)
|
codeshield/cli.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI entry point for CodeShield
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
parser = argparse.ArgumentParser(
|
|
11
|
+
description="CodeShield - The Complete AI Coding Safety Net"
|
|
12
|
+
)
|
|
13
|
+
subparsers = parser.add_subparsers(dest="command", help="Commands")
|
|
14
|
+
|
|
15
|
+
# serve command
|
|
16
|
+
serve_parser = subparsers.add_parser("serve", help="Start MCP server")
|
|
17
|
+
serve_parser.add_argument("--port", type=int, default=8080, help="Server port")
|
|
18
|
+
serve_parser.add_argument("--host", default="localhost", help="Server host")
|
|
19
|
+
|
|
20
|
+
# verify command
|
|
21
|
+
verify_parser = subparsers.add_parser("verify", help="Verify code")
|
|
22
|
+
verify_parser.add_argument("file", help="File to verify")
|
|
23
|
+
|
|
24
|
+
# style command
|
|
25
|
+
style_parser = subparsers.add_parser("style", help="Check code style against codebase")
|
|
26
|
+
style_parser.add_argument("file", help="File to check")
|
|
27
|
+
style_parser.add_argument("--codebase", default=".", help="Codebase path")
|
|
28
|
+
|
|
29
|
+
args = parser.parse_args()
|
|
30
|
+
|
|
31
|
+
if args.command == "serve":
|
|
32
|
+
from codeshield.mcp.server import run_server
|
|
33
|
+
run_server(host=args.host, port=args.port)
|
|
34
|
+
elif args.command == "verify":
|
|
35
|
+
from codeshield.trustgate.checker import verify_file
|
|
36
|
+
result = verify_file(args.file)
|
|
37
|
+
print(result)
|
|
38
|
+
elif args.command == "style":
|
|
39
|
+
from codeshield.styleforge.corrector import check_style
|
|
40
|
+
result = check_style(args.file, args.codebase)
|
|
41
|
+
print(result)
|
|
42
|
+
else:
|
|
43
|
+
parser.print_help()
|
|
44
|
+
sys.exit(1)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""ContextVault - Context memory module"""
|