David Prince
production: clean source snapshot — no history bloat
71b4454
Raw
History Blame Contribute Delete
4.92 kB
import asyncio
import os
import shutil
import subprocess
from typing import Any, Dict, List
class FileExplorerAPI:
"""Provides file system operations for browser workspace integration."""
def __init__(self, root_dir: str = "."):
self.root_dir = os.path.abspath(root_dir)
def _resolve(self, relative_path: str) -> str:
full_path = os.path.abspath(
os.path.join(self.root_dir, relative_path)
)
if not full_path.startswith(self.root_dir):
raise PermissionError("Access outside workspace boundary denied")
return full_path
def list_files(self, relative_path: str = "") -> List[Dict[str, Any]]:
target = self._resolve(relative_path)
if not os.path.exists(target):
return []
# Requested path is a file
if os.path.isfile(target):
rel = os.path.relpath(target, self.root_dir)
return [{
"name": os.path.basename(target),
"path": rel,
"is_directory": False,
"size": os.path.getsize(target),
}]
# Requested path is not a directory
if not os.path.isdir(target):
return []
entries: List[Dict[str, Any]] = []
for name in sorted(os.listdir(target)):
if name.startswith("."):
continue
if name in (
"__pycache__",
"node_modules",
"venv",
".venv",
):
continue
item_path = os.path.join(target, name)
entries.append({
"name": name,
"path": os.path.relpath(item_path, self.root_dir),
"is_directory": os.path.isdir(item_path),
"size": (
os.path.getsize(item_path)
if os.path.isfile(item_path)
else 0
),
})
return entries
def read_file(self, relative_path: str) -> str:
target = self._resolve(relative_path)
with open(target, "r", encoding="utf-8") as f:
return f.read()
def write_file(self, relative_path: str, content: str) -> bool:
target = self._resolve(relative_path)
os.makedirs(os.path.dirname(target), exist_ok=True)
with open(target, "w", encoding="utf-8") as f:
f.write(content)
return True
def delete_file(self, relative_path: str) -> bool:
target = self._resolve(relative_path)
if os.path.isfile(target):
os.remove(target)
return True
if os.path.isdir(target):
shutil.rmtree(target)
return True
return False
class TerminalAPI:
"""Executes asynchronous terminal operations."""
def __init__(self, root_dir: str = "."):
self.root_dir = os.path.abspath(root_dir)
async def run_command(self, command: str) -> Dict[str, Any]:
proc = await asyncio.create_subprocess_shell(
command,
cwd=self.root_dir,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, stderr = await proc.communicate()
return {
"exit_code": proc.returncode,
"stdout": stdout.decode(errors="replace"),
"stderr": stderr.decode(errors="replace"),
}
class GitAPI:
"""Git wrapper."""
def __init__(self, root_dir: str = "."):
self.root_dir = os.path.abspath(root_dir)
def _git(self, *args) -> Dict[str, Any]:
res = subprocess.run(
["git", *args],
cwd=self.root_dir,
capture_output=True,
text=True,
)
return {
"exit_code": res.returncode,
"stdout": res.stdout.strip(),
"stderr": res.stderr.strip(),
}
def status(self):
return self._git("status", "--porcelain")
def commit(self, message: str):
self._git("add", "-A")
return self._git("commit", "-m", message)
def current_branch(self):
result = self._git("branch", "--show-current")
return result["stdout"] or "main"
class BrowserWorkspaceBackend:
"""Workspace backend."""
def __init__(self, root_dir: str = "."):
self.file_api = FileExplorerAPI(root_dir)
self.terminal_api = TerminalAPI(root_dir)
self.git_api = GitAPI(root_dir)
def get_build_status(self):
return {
"status": "ready",
"target": "production",
}
def get_preview_url(self, port: int = 3000):
return f"http://localhost:{port}/preview"
def deploy(self, environment: str = "staging"):
return {
"deployment_id": "dep_12345",
"environment": environment,
"status": "deployed",
}