mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-02-01 23:13:36 +00:00
feat: move autocoder runtime files into .autocoder/ subdirectory
Add centralized path resolution module (autocoder_paths.py) that consolidates all autocoder-generated file paths behind a dual-path strategy: check .autocoder/X first, fall back to root-level X for backward compatibility, default to .autocoder/X for new projects. Key changes: - New autocoder_paths.py with dual-path resolution for features.db, assistant.db, lock files, settings, prompts dir, and progress cache - migrate_project_layout() safely moves old-layout projects to new layout with SQLite WAL flush and integrity verification - Updated 22 files to delegate path construction to autocoder_paths - Reset/delete logic cleans both old and new file locations - Orphan lock cleanup checks both locations per project - Migration called automatically at agent start in autonomous_agent_demo.py - Updated markdown commands/skills to reference .autocoder/prompts/ - CLAUDE.md documentation updated with new project structure Files at project root that remain unchanged: - CLAUDE.md (Claude SDK reads from cwd via setting_sources=["project"]) - app_spec.txt root copy (agent templates reference it via cat) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ API endpoints for dev server control (start/stop) and configuration.
|
||||
Uses project registry for path lookups and project_config for command detection.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
@@ -33,6 +34,9 @@ if str(_root) not in sys.path:
|
||||
sys.path.insert(0, str(_root))
|
||||
|
||||
from registry import get_project_path as registry_get_project_path
|
||||
from security import extract_commands, get_effective_commands, is_command_allowed
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_project_path(project_name: str) -> Path | None:
|
||||
@@ -106,6 +110,45 @@ def get_project_devserver_manager(project_name: str):
|
||||
return get_devserver_manager(project_name, project_dir)
|
||||
|
||||
|
||||
def validate_dev_command(command: str, project_dir: Path) -> None:
|
||||
"""
|
||||
Validate a dev server command against the security allowlist.
|
||||
|
||||
Extracts all commands from the shell string and checks each against
|
||||
the effective allowlist (global + org + project). Raises HTTPException
|
||||
if any command is blocked or not allowed.
|
||||
|
||||
Args:
|
||||
command: The shell command string to validate
|
||||
project_dir: Project directory for loading project-level allowlists
|
||||
|
||||
Raises:
|
||||
HTTPException 400: If the command fails validation
|
||||
"""
|
||||
commands = extract_commands(command)
|
||||
if not commands:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Could not parse command for security validation"
|
||||
)
|
||||
|
||||
allowed_commands, blocked_commands = get_effective_commands(project_dir)
|
||||
|
||||
for cmd in commands:
|
||||
if cmd in blocked_commands:
|
||||
logger.warning("Blocked dev server command '%s' (in blocklist) for project dir %s", cmd, project_dir)
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Command '{cmd}' is blocked and cannot be used as a dev server command"
|
||||
)
|
||||
if not is_command_allowed(cmd, allowed_commands):
|
||||
logger.warning("Rejected dev server command '%s' (not in allowlist) for project dir %s", cmd, project_dir)
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Command '{cmd}' is not in the allowed commands list"
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Endpoints
|
||||
# ============================================================================
|
||||
@@ -167,7 +210,10 @@ async def start_devserver(
|
||||
detail="No dev command available. Configure a custom command or ensure project type can be detected."
|
||||
)
|
||||
|
||||
# Now command is definitely str
|
||||
# Validate command against security allowlist before execution
|
||||
validate_dev_command(command, project_dir)
|
||||
|
||||
# Now command is definitely str and validated
|
||||
success, message = await manager.start(command)
|
||||
|
||||
return DevServerActionResponse(
|
||||
@@ -258,6 +304,9 @@ async def update_devserver_config(
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
else:
|
||||
# Validate command against security allowlist before persisting
|
||||
validate_dev_command(update.custom_command, project_dir)
|
||||
|
||||
# Set the custom command
|
||||
try:
|
||||
set_dev_command(project_dir, update.custom_command)
|
||||
|
||||
Reference in New Issue
Block a user