Mayank2027 commited on
Commit
e1d6afa
·
verified ·
1 Parent(s): d706219

Update backend/main.py

Browse files
Files changed (1) hide show
  1. backend/main.py +227 -65
backend/main.py CHANGED
@@ -1,4 +1,10 @@
1
- import os, json, asyncio, subprocess, uuid
 
 
 
 
 
 
2
  from contextlib import asynccontextmanager
3
  from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect, BackgroundTasks
4
  from fastapi.responses import FileResponse, StreamingResponse
@@ -20,9 +26,13 @@ def get_codebase_index():
20
  from codebase_index import CodebaseIndex as CBI
21
  CodebaseIndex = CBI
22
  except ImportError:
23
- raise HTTPException(status_code=503, detail="Codebase indexing unavailable – rebuild with updated sentence-transformers")
 
 
 
24
  return CodebaseIndex
25
 
 
26
  # Auto-index sandbox on startup
27
  async def startup_index():
28
  try:
@@ -33,16 +43,20 @@ async def startup_index():
33
  except Exception as e:
34
  print(f"Startup indexing failed: {e}")
35
 
 
36
  @asynccontextmanager
37
  async def lifespan(app: FastAPI):
38
  await startup_index()
39
  yield
40
 
 
41
  app = FastAPI(title="Codeki API", lifespan=lifespan)
42
 
43
- indexers = {}
44
- agents = {}
45
  STATIC_DIR = "static"
 
 
46
 
47
  # ---------- API Models ----------
48
  class ChatRequest(BaseModel):
@@ -51,13 +65,15 @@ class ChatRequest(BaseModel):
51
  context_files: Optional[List[str]] = None
52
  project_path: str = "/app/sandbox"
53
 
 
54
  class AgentTaskRequest(BaseModel):
55
  prompt: str
56
  model: str = "openrouter-anthropic/claude-3-sonnet"
57
- project_path: str
58
  auto_approve: bool = False
59
  background: bool = False
60
 
 
61
  class AutocompleteRequest(BaseModel):
62
  prefix: str
63
  suffix: str = ""
@@ -66,61 +82,101 @@ class AutocompleteRequest(BaseModel):
66
  max_tokens: int = 64
67
  model: str = "groq-llama-3.1-8b-instant"
68
 
 
69
  class IndexRequest(BaseModel):
70
- project_path: str
 
71
 
72
  class BugFixRequest(BaseModel):
73
  file_path: str
74
  error_output: str
75
- project_path: str
 
76
 
77
  class ReviewRequest(BaseModel):
78
  pr_diff: str
79
- project_path: str
 
80
 
81
  class ApplyRequest(BaseModel):
82
  task_id: str
83
  approved_indices: List[int]
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  # ---------- API Routes ----------
86
  @app.get("/api/models")
87
  async def get_models():
88
  return get_available_models()
89
 
 
90
  @app.post("/api/chat")
91
  async def chat(req: ChatRequest):
92
  context = ""
 
 
93
  if req.context_files:
94
  for f in req.context_files:
95
  if f == "__git__":
96
- import git
97
  try:
98
- repo = git.Repo(req.project_path)
 
99
  diff = repo.git.diff(repo.head.commit.tree) if repo.head.is_valid() else ""
100
- context += f"\n--- Git Diff ---\n{diff}\n"
101
- except:
102
  pass
103
  else:
104
- p = Path(req.project_path) / f
105
- if p.exists():
106
- context += f"\n--- {f} ---\n{p.read_text()}\n"
107
- req.messages.insert(0, {"role": "system", "content": f"Relevant codebase context:\n{context}"})
 
 
 
 
 
 
 
 
 
 
108
  async def event_stream():
109
  try:
110
- async for token in route_chat(req.model, req.messages):
111
- yield f"data: {json.dumps({'token': token})}\n\n"
112
- yield "data: [DONE]\n\n"
113
  except Exception as e:
114
- yield f"data: {json.dumps({'token': f'ERROR: {str(e)}'})}\n\n"
115
- yield "data: [DONE]\n\n"
 
116
  return StreamingResponse(event_stream(), media_type="text/event-stream")
117
 
 
118
  @app.post("/api/agent/plan")
119
  async def agent_plan(req: AgentTaskRequest):
120
- agent = CodingAgent(project_path=req.project_path, model=req.model)
 
121
  changes = await agent.plan_task(req.prompt)
122
- agents["default"] = agent
123
- return {"changes": changes}
 
 
124
 
125
  @app.post("/api/agent/apply")
126
  async def agent_apply(req: ApplyRequest):
@@ -130,9 +186,11 @@ async def agent_apply(req: ApplyRequest):
130
  await agent.apply_changes(req.approved_indices)
131
  return {"status": agent.status, "logs": agent.logs}
132
 
 
133
  @app.post("/api/agent/run")
134
  async def run_agent(req: AgentTaskRequest, background_tasks: BackgroundTasks):
135
- agent = CodingAgent(project_path=req.project_path, model=req.model)
 
136
  if req.background:
137
  task_id = str(uuid.uuid4())
138
  agents[task_id] = agent
@@ -141,128 +199,226 @@ async def run_agent(req: AgentTaskRequest, background_tasks: BackgroundTasks):
141
  else:
142
  async def event_stream():
143
  async for event in agent.execute_task_stream(req.prompt, auto_approve=req.auto_approve):
144
- yield f"data: {json.dumps(event)}\n\n"
145
- yield "data: [DONE]\n\n"
146
  return StreamingResponse(event_stream(), media_type="text/event-stream")
147
 
 
148
  @app.post("/api/autocomplete")
149
  async def autocomplete(req: AutocompleteRequest):
150
  suggestions = await generate_autocomplete(req)
151
  return {"completions": suggestions}
152
 
 
153
  @app.post("/api/index")
154
  async def index_project(req: IndexRequest):
 
155
  CBI = get_codebase_index()
156
- idx = CBI(req.project_path)
157
  idx.build_index()
158
- indexers[req.project_path] = idx
159
  return {"status": "indexed", "files": len(idx.file_list)}
160
 
 
161
  @app.post("/api/search")
162
  async def search_codebase(query: str, project_path: str = "/app/sandbox", top_k: int = 5):
163
- if project_path not in indexers:
 
164
  CBI = get_codebase_index()
165
- idx = CBI(project_path)
166
  try:
167
  idx.build_index()
168
  except Exception as e:
169
  raise HTTPException(503, f"Indexing failed: {str(e)}")
170
- indexers[project_path] = idx
171
- idx = indexers[project_path]
 
172
  results = idx.search(query, top_k)
173
  return {"results": results}
174
 
 
175
  @app.post("/api/bugfix")
176
  async def bugfix(req: BugFixRequest):
177
- agent = CodingAgent(project_path=req.project_path, model="openrouter-anthropic/claude-3-sonnet")
 
 
 
 
178
  result = await agent.fix_error(req.file_path, req.error_output)
179
  return result
180
 
 
181
  @app.post("/api/review")
182
  async def review_code(req: ReviewRequest):
183
- agent = CodingAgent(project_path=req.project_path, model="openrouter-anthropic/claude-3-sonnet")
 
 
 
 
184
  review = await agent.review_pr(req.pr_diff)
185
  return {"review": review}
186
 
 
187
  @app.post("/api/git")
188
- async def git_operation(action: str, project_path: str, args: Optional[dict] = None):
189
- import git
190
  try:
191
- repo = git.Repo(project_path)
192
- if action == "commit":
 
 
 
193
  repo.git.add(A=True)
194
  repo.git.commit(m=args.get("message", "auto commit"))
195
- elif action == "log":
 
 
196
  logs = list(repo.iter_commits(max_count=10))
197
- return {"log": [{"message": c.message, "hash": c.hexsha} for c in logs]}
198
- elif action == "branch":
 
 
 
 
 
 
 
 
 
 
 
199
  branches = [b.name for b in repo.branches]
200
  return {"branches": branches}
201
- elif action == "diff":
202
- diff_output = repo.git.diff(repo.head.commit.tree) if repo.head.is_valid() else ""
 
 
 
203
  return {"diff": diff_output}
 
 
 
 
204
  else:
205
- raise HTTPException(400, "Unknown action")
 
206
  except Exception as e:
207
- raise HTTPException(400, str(e))
 
208
 
209
  @app.websocket("/ws/terminal")
210
  async def terminal_ws(websocket: WebSocket):
211
  await websocket.accept()
212
  proc = None
 
 
 
 
 
 
 
 
 
 
 
213
  try:
214
  while True:
215
  cmd = await websocket.receive_text()
 
216
  if cmd.startswith("exec:"):
217
  command = cmd[5:]
218
  proc = await asyncio.create_subprocess_shell(
219
  command,
220
  stdout=asyncio.subprocess.PIPE,
221
  stderr=asyncio.subprocess.PIPE,
222
- cwd="/app/sandbox"
 
 
 
 
 
223
  )
224
- stdout, stderr = await proc.communicate()
225
- output = stdout.decode() + stderr.decode()
226
- await websocket.send_text(output)
227
- elif cmd == "ctrl-c" and proc:
 
228
  proc.terminate()
 
 
 
 
 
 
229
  except WebSocketDisconnect:
230
  pass
 
 
 
 
 
 
 
 
231
 
232
  @app.get("/api/agent/tasks")
233
  async def list_agent_tasks():
234
  return {"tasks": list(agents.keys())}
235
 
236
- @app.post("/api/agent/task/{task_id}")
 
237
  async def get_agent_status(task_id: str):
238
  agent = agents.get(task_id)
239
  if not agent:
240
  raise HTTPException(404, "Task not found")
241
  return {"status": agent.status, "logs": agent.logs}
242
 
 
243
  @app.get("/api/files")
244
  async def list_files(project_path: str = "/app/sandbox"):
245
- def build_tree(path):
 
 
246
  tree = []
247
- for item in sorted(os.listdir(path)):
248
- if item.startswith('.') or item == '__pycache__':
249
- continue
250
- full = os.path.join(path, item)
251
- node = {"name": item, "path": full.replace(project_path, "").lstrip("/"), "type": "folder" if os.path.isdir(full) else "file"}
252
- if os.path.isdir(full):
253
- node["children"] = build_tree(full)
254
- tree.append(node)
 
 
 
 
 
 
 
 
 
 
255
  return tree
 
256
  try:
257
- tree = build_tree(project_path)
258
  return tree
259
  except Exception as e:
260
  raise HTTPException(400, str(e))
261
 
262
- # ---------- Serve Frontend (catch-all, must be last) ----------
 
 
 
 
 
 
 
 
 
263
  @app.get("/{full_path:path}")
264
  async def serve_frontend(full_path: str):
265
- if full_path.startswith("api"):
266
  raise HTTPException(404)
267
  file_path = os.path.join(STATIC_DIR, full_path)
268
  if os.path.isfile(file_path):
@@ -272,6 +428,12 @@ async def serve_frontend(full_path: str):
272
  return FileResponse(index_path)
273
  raise HTTPException(404, detail="Not found")
274
 
 
275
  if __name__ == "__main__":
276
  import uvicorn
277
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
 
 
 
 
 
1
+
2
+ # Fixed main.py
3
+ main_py = '''import os
4
+ import json
5
+ import asyncio
6
+ import subprocess
7
+ import uuid
8
  from contextlib import asynccontextmanager
9
  from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect, BackgroundTasks
10
  from fastapi.responses import FileResponse, StreamingResponse
 
26
  from codebase_index import CodebaseIndex as CBI
27
  CodebaseIndex = CBI
28
  except ImportError:
29
+ raise HTTPException(
30
+ status_code=503,
31
+ detail="Codebase indexing unavailable – rebuild with updated sentence-transformers"
32
+ )
33
  return CodebaseIndex
34
 
35
+
36
  # Auto-index sandbox on startup
37
  async def startup_index():
38
  try:
 
43
  except Exception as e:
44
  print(f"Startup indexing failed: {e}")
45
 
46
+
47
  @asynccontextmanager
48
  async def lifespan(app: FastAPI):
49
  await startup_index()
50
  yield
51
 
52
+
53
  app = FastAPI(title="Codeki API", lifespan=lifespan)
54
 
55
+ indexers: Dict[str, Any] = {}
56
+ agents: Dict[str, CodingAgent] = {}
57
  STATIC_DIR = "static"
58
+ SANDBOX_ROOT = "/app/sandbox"
59
+
60
 
61
  # ---------- API Models ----------
62
  class ChatRequest(BaseModel):
 
65
  context_files: Optional[List[str]] = None
66
  project_path: str = "/app/sandbox"
67
 
68
+
69
  class AgentTaskRequest(BaseModel):
70
  prompt: str
71
  model: str = "openrouter-anthropic/claude-3-sonnet"
72
+ project_path: str = "/app/sandbox"
73
  auto_approve: bool = False
74
  background: bool = False
75
 
76
+
77
  class AutocompleteRequest(BaseModel):
78
  prefix: str
79
  suffix: str = ""
 
82
  max_tokens: int = 64
83
  model: str = "groq-llama-3.1-8b-instant"
84
 
85
+
86
  class IndexRequest(BaseModel):
87
+ project_path: str = "/app/sandbox"
88
+
89
 
90
  class BugFixRequest(BaseModel):
91
  file_path: str
92
  error_output: str
93
+ project_path: str = "/app/sandbox"
94
+
95
 
96
  class ReviewRequest(BaseModel):
97
  pr_diff: str
98
+ project_path: str = "/app/sandbox"
99
+
100
 
101
  class ApplyRequest(BaseModel):
102
  task_id: str
103
  approved_indices: List[int]
104
 
105
+
106
+ class GitRequest(BaseModel):
107
+ action: str
108
+ project_path: str = "/app/sandbox"
109
+ args: Optional[dict] = None
110
+
111
+
112
+ def _validate_project_path(project_path: str) -> str:
113
+ """Validate and resolve project path to prevent directory traversal."""
114
+ real_path = os.path.realpath(project_path)
115
+ real_sandbox = os.path.realpath(SANDBOX_ROOT)
116
+ if not real_path.startswith(real_sandbox):
117
+ raise HTTPException(status_code=403, detail="Access denied: path outside sandbox")
118
+ if not os.path.exists(real_path):
119
+ raise HTTPException(status_code=404, detail="Project path not found")
120
+ return real_path
121
+
122
+
123
  # ---------- API Routes ----------
124
  @app.get("/api/models")
125
  async def get_models():
126
  return get_available_models()
127
 
128
+
129
  @app.post("/api/chat")
130
  async def chat(req: ChatRequest):
131
  context = ""
132
+ safe_path = _validate_project_path(req.project_path)
133
+
134
  if req.context_files:
135
  for f in req.context_files:
136
  if f == "__git__":
 
137
  try:
138
+ import git
139
+ repo = git.Repo(safe_path)
140
  diff = repo.git.diff(repo.head.commit.tree) if repo.head.is_valid() else ""
141
+ context += f"\\n--- Git Diff ---\\n{diff}\\n"
142
+ except Exception:
143
  pass
144
  else:
145
+ p = Path(safe_path) / f
146
+ try:
147
+ real_p = os.path.realpath(p)
148
+ if not real_p.startswith(os.path.realpath(SANDBOX_ROOT)):
149
+ continue
150
+ if p.exists() and p.is_file():
151
+ context += f"\\n--- {f} ---\\n{p.read_text(encoding='utf-8')}\\n"
152
+ except Exception:
153
+ pass
154
+
155
+ messages = req.messages.copy()
156
+ if context:
157
+ messages.insert(0, {"role": "system", "content": f"Relevant codebase context:\\n{context}"})
158
+
159
  async def event_stream():
160
  try:
161
+ async for token in route_chat(req.model, messages):
162
+ yield f"data: {json.dumps({'token': token})}\\n\\n"
163
+ yield "data: [DONE]\\n\\n"
164
  except Exception as e:
165
+ yield f"data: {json.dumps({'token': f'ERROR: {str(e)}'})}\\n\\n"
166
+ yield "data: [DONE]\\n\\n"
167
+
168
  return StreamingResponse(event_stream(), media_type="text/event-stream")
169
 
170
+
171
  @app.post("/api/agent/plan")
172
  async def agent_plan(req: AgentTaskRequest):
173
+ safe_path = _validate_project_path(req.project_path)
174
+ agent = CodingAgent(project_path=safe_path, model=req.model)
175
  changes = await agent.plan_task(req.prompt)
176
+ task_id = str(uuid.uuid4())
177
+ agents[task_id] = agent
178
+ return {"task_id": task_id, "changes": changes}
179
+
180
 
181
  @app.post("/api/agent/apply")
182
  async def agent_apply(req: ApplyRequest):
 
186
  await agent.apply_changes(req.approved_indices)
187
  return {"status": agent.status, "logs": agent.logs}
188
 
189
+
190
  @app.post("/api/agent/run")
191
  async def run_agent(req: AgentTaskRequest, background_tasks: BackgroundTasks):
192
+ safe_path = _validate_project_path(req.project_path)
193
+ agent = CodingAgent(project_path=safe_path, model=req.model)
194
  if req.background:
195
  task_id = str(uuid.uuid4())
196
  agents[task_id] = agent
 
199
  else:
200
  async def event_stream():
201
  async for event in agent.execute_task_stream(req.prompt, auto_approve=req.auto_approve):
202
+ yield f"data: {json.dumps(event)}\\n\\n"
203
+ yield "data: [DONE]\\n\\n"
204
  return StreamingResponse(event_stream(), media_type="text/event-stream")
205
 
206
+
207
  @app.post("/api/autocomplete")
208
  async def autocomplete(req: AutocompleteRequest):
209
  suggestions = await generate_autocomplete(req)
210
  return {"completions": suggestions}
211
 
212
+
213
  @app.post("/api/index")
214
  async def index_project(req: IndexRequest):
215
+ safe_path = _validate_project_path(req.project_path)
216
  CBI = get_codebase_index()
217
+ idx = CBI(safe_path)
218
  idx.build_index()
219
+ indexers[safe_path] = idx
220
  return {"status": "indexed", "files": len(idx.file_list)}
221
 
222
+
223
  @app.post("/api/search")
224
  async def search_codebase(query: str, project_path: str = "/app/sandbox", top_k: int = 5):
225
+ safe_path = _validate_project_path(project_path)
226
+ if safe_path not in indexers:
227
  CBI = get_codebase_index()
228
+ idx = CBI(safe_path)
229
  try:
230
  idx.build_index()
231
  except Exception as e:
232
  raise HTTPException(503, f"Indexing failed: {str(e)}")
233
+ indexers[safe_path] = idx
234
+
235
+ idx = indexers[safe_path]
236
  results = idx.search(query, top_k)
237
  return {"results": results}
238
 
239
+
240
  @app.post("/api/bugfix")
241
  async def bugfix(req: BugFixRequest):
242
+ safe_path = _validate_project_path(req.project_path)
243
+ agent = CodingAgent(
244
+ project_path=safe_path,
245
+ model="openrouter-anthropic/claude-3-sonnet"
246
+ )
247
  result = await agent.fix_error(req.file_path, req.error_output)
248
  return result
249
 
250
+
251
  @app.post("/api/review")
252
  async def review_code(req: ReviewRequest):
253
+ safe_path = _validate_project_path(req.project_path)
254
+ agent = CodingAgent(
255
+ project_path=safe_path,
256
+ model="openrouter-anthropic/claude-3-sonnet"
257
+ )
258
  review = await agent.review_pr(req.pr_diff)
259
  return {"review": review}
260
 
261
+
262
  @app.post("/api/git")
263
+ async def git_operation(req: GitRequest):
264
+ safe_path = _validate_project_path(req.project_path)
265
  try:
266
+ import git
267
+ repo = git.Repo(safe_path)
268
+ args = req.args or {}
269
+
270
+ if req.action == "commit":
271
  repo.git.add(A=True)
272
  repo.git.commit(m=args.get("message", "auto commit"))
273
+ return {"status": "committed"}
274
+
275
+ elif req.action == "log":
276
  logs = list(repo.iter_commits(max_count=10))
277
+ return {
278
+ "log": [
279
+ {
280
+ "message": c.message.strip(),
281
+ "hash": c.hexsha,
282
+ "author": str(c.author),
283
+ "date": str(c.committed_datetime),
284
+ }
285
+ for c in logs
286
+ ]
287
+ }
288
+
289
+ elif req.action == "branch":
290
  branches = [b.name for b in repo.branches]
291
  return {"branches": branches}
292
+
293
+ elif req.action == "diff":
294
+ diff_output = ""
295
+ if repo.head.is_valid():
296
+ diff_output = repo.git.diff(repo.head.commit.tree)
297
  return {"diff": diff_output}
298
+
299
+ elif req.action == "status":
300
+ return {"status": repo.git.status()}
301
+
302
  else:
303
+ raise HTTPException(400, f"Unknown git action: {req.action}")
304
+
305
  except Exception as e:
306
+ raise HTTPException(400, detail=str(e))
307
+
308
 
309
  @app.websocket("/ws/terminal")
310
  async def terminal_ws(websocket: WebSocket):
311
  await websocket.accept()
312
  proc = None
313
+
314
+ async def read_stream(stream, websocket: WebSocket):
315
+ try:
316
+ while True:
317
+ line = await stream.readline()
318
+ if not line:
319
+ break
320
+ await websocket.send_text(line.decode(errors='replace'))
321
+ except Exception:
322
+ pass
323
+
324
  try:
325
  while True:
326
  cmd = await websocket.receive_text()
327
+
328
  if cmd.startswith("exec:"):
329
  command = cmd[5:]
330
  proc = await asyncio.create_subprocess_shell(
331
  command,
332
  stdout=asyncio.subprocess.PIPE,
333
  stderr=asyncio.subprocess.PIPE,
334
+ cwd=SANDBOX_ROOT,
335
+ )
336
+ # Stream stdout and stderr concurrently
337
+ await asyncio.gather(
338
+ read_stream(proc.stdout, websocket),
339
+ read_stream(proc.stderr, websocket),
340
  )
341
+ await proc.wait()
342
+ await websocket.send_text(f"\\n[exit code: {proc.returncode}]\\n")
343
+ proc = None
344
+
345
+ elif cmd == "ctrl-c" and proc and proc.returncode is None:
346
  proc.terminate()
347
+ try:
348
+ await asyncio.wait_for(proc.wait(), timeout=2.0)
349
+ except asyncio.TimeoutError:
350
+ proc.kill()
351
+ proc = None
352
+
353
  except WebSocketDisconnect:
354
  pass
355
+ finally:
356
+ if proc and proc.returncode is None:
357
+ proc.kill()
358
+ try:
359
+ await asyncio.wait_for(proc.wait(), timeout=1.0)
360
+ except asyncio.TimeoutError:
361
+ pass
362
+
363
 
364
  @app.get("/api/agent/tasks")
365
  async def list_agent_tasks():
366
  return {"tasks": list(agents.keys())}
367
 
368
+
369
+ @app.get("/api/agent/task/{task_id}")
370
  async def get_agent_status(task_id: str):
371
  agent = agents.get(task_id)
372
  if not agent:
373
  raise HTTPException(404, "Task not found")
374
  return {"status": agent.status, "logs": agent.logs}
375
 
376
+
377
  @app.get("/api/files")
378
  async def list_files(project_path: str = "/app/sandbox"):
379
+ safe_path = _validate_project_path(project_path)
380
+
381
+ def build_tree(path: str):
382
  tree = []
383
+ try:
384
+ for item in sorted(os.listdir(path)):
385
+ if item.startswith('.') or item == '__pycache__':
386
+ continue
387
+ full = os.path.join(path, item)
388
+ real_full = os.path.realpath(full)
389
+ if not real_full.startswith(os.path.realpath(SANDBOX_ROOT)):
390
+ continue
391
+ node = {
392
+ "name": item,
393
+ "path": full.replace(safe_path, "").lstrip("/"),
394
+ "type": "folder" if os.path.isdir(full) else "file",
395
+ }
396
+ if os.path.isdir(full):
397
+ node["children"] = build_tree(full)
398
+ tree.append(node)
399
+ except PermissionError:
400
+ pass
401
  return tree
402
+
403
  try:
404
+ tree = build_tree(safe_path)
405
  return tree
406
  except Exception as e:
407
  raise HTTPException(400, str(e))
408
 
409
+
410
+ # ---------- Serve Frontend ----------
411
+ @app.get("/")
412
+ async def serve_index():
413
+ index_path = os.path.join(STATIC_DIR, "index.html")
414
+ if os.path.isfile(index_path):
415
+ return FileResponse(index_path)
416
+ raise HTTPException(404, detail="Frontend not built")
417
+
418
+
419
  @app.get("/{full_path:path}")
420
  async def serve_frontend(full_path: str):
421
+ if full_path.startswith("api/") or full_path.startswith("ws/"):
422
  raise HTTPException(404)
423
  file_path = os.path.join(STATIC_DIR, full_path)
424
  if os.path.isfile(file_path):
 
428
  return FileResponse(index_path)
429
  raise HTTPException(404, detail="Not found")
430
 
431
+
432
  if __name__ == "__main__":
433
  import uvicorn
434
+ uvicorn.run(app, host="0.0.0.0", port=7860)
435
+ '''
436
+
437
+ with open('/mnt/agents/output/main.py', 'w') as f:
438
+ f.write(main_py)
439
+ print("main.py written successfully")