diff --git a/backend/apps/app_builder/App.py b/backend/apps/app_builder/App.py index 0116f3c4..458b7495 100644 --- a/backend/apps/app_builder/App.py +++ b/backend/apps/app_builder/App.py @@ -9,11 +9,6 @@ class App(BaseModel): name: str description: str = "" icon: str = "view_quilt" - input_schema: dict[str, Any] = Field(default_factory=lambda: { - "type": "object", - "properties": {}, - "required": [], - }) files: dict[str, str] = Field(default_factory=dict) thumbnail: Optional[str] = None created_at: str = Field(default_factory=lambda: datetime.now().isoformat()) @@ -24,11 +19,6 @@ class AppCreate(BaseModel): name: str description: str = "" icon: str = "view_quilt" - input_schema: dict[str, Any] = Field(default_factory=lambda: { - "type": "object", - "properties": {}, - "required": [], - }) files: dict[str, str] = Field(default_factory=dict) thumbnail: Optional[str] = None @@ -37,21 +27,18 @@ class AppUpdate(BaseModel): name: Optional[str] = None description: Optional[str] = None icon: Optional[str] = None - input_schema: Optional[dict[str, Any]] = None files: Optional[dict[str, str]] = None thumbnail: Optional[str] = None class AppExecute(BaseModel): app_id: str - input_data: dict[str, Any] = Field(default_factory=dict) class AppExecuteResult(BaseModel): app_id: str app_name: str frontend_code: str - input_data: dict[str, Any] backend_result: Optional[dict[str, Any]] = None stdout: Optional[str] = None stderr: Optional[str] = None diff --git a/backend/apps/app_builder/app_builder.py b/backend/apps/app_builder/app_builder.py index 0de2e998..8989fc52 100644 --- a/backend/apps/app_builder/app_builder.py +++ b/backend/apps/app_builder/app_builder.py @@ -18,9 +18,7 @@ from backend.apps.app_builder.App import ( from backend.config.paths import DB_ROOT from backend.apps.app_builder.executor import execute_backend_code from backend.apps.app_builder.templates import APP_BUILDER_SKILL, APP_BUILDER_TEMPLATE_FILES -from backend.apps.app_builder.helpers import ( - validate_against_schema, inject_data_into_html, decode_data_param, walk_directory, -) +from backend.apps.app_builder.helpers import walk_directory APP_BUILDER_DIR = os.path.join(DB_ROOT, "app_builder") APP_BUILDER_WORKSPACE_DIR = os.path.join(APP_BUILDER_DIR, "workspace") @@ -42,7 +40,7 @@ _store = PydanticStore[App](model_cls=App, data_dir=APP_BUILDER_DIR, not_found_d # --------------------------------------------------------------------------- @app_builder.router.get("/workspace/{workspace_id}/serve/{filepath:path}") -async def serve_workspace_file(workspace_id: str, filepath: str, _d: str = ""): +async def serve_workspace_file(workspace_id: str, filepath: str): folder = os.path.join(APP_BUILDER_WORKSPACE_DIR, workspace_id) full_path = os.path.normpath(os.path.join(folder, filepath)) if not full_path.startswith(os.path.normpath(folder)): @@ -51,22 +49,16 @@ async def serve_workspace_file(workspace_id: str, filepath: str, _d: str = ""): raise HTTPException(status_code=404, detail="File not found") with open(full_path) as f: content = f.read() - if filepath == "index.html": - input_json, result_json = decode_data_param(_d) if _d else ("{}", "null") - content = inject_data_into_html(content, input_json, result_json) mime, _ = mimetypes.guess_type(filepath) return Response(content=content, media_type=mime or "text/plain") @app_builder.router.get("/{app_id}/serve/{filepath:path}") -async def serve_app_file(app_id: str, filepath: str, _d: str = ""): +async def serve_app_file(app_id: str, filepath: str): app = _store.load(app_id) content = app.files.get(filepath) if content is None: raise HTTPException(status_code=404, detail="File not found in app") - if filepath == "index.html": - input_json, result_json = decode_data_param(_d) if _d else ("{}", "null") - content = inject_data_into_html(content, input_json, result_json) mime, _ = mimetypes.guess_type(filepath) return Response(content=content, media_type=mime or "text/plain") @@ -168,8 +160,8 @@ async def create_app(body: AppCreate): now = datetime.now().isoformat() app = App( name=body.name, description=body.description, icon=body.icon, - input_schema=body.input_schema, files=body.files, - thumbnail=body.thumbnail, created_at=now, updated_at=now, + files=body.files, thumbnail=body.thumbnail, + created_at=now, updated_at=now, ) _store.save(app) return {"ok": True, "app": app.model_dump()} @@ -199,14 +191,6 @@ async def delete_app(app_id: str): @app_builder.router.post("/execute") async def execute_app(body: AppExecute): app = _store.load(body.app_id) - validation_err = validate_against_schema(body.input_data, app.input_schema) - if validation_err: - return AppExecuteResult( - app_id=app.id, app_name=app.name, - frontend_code=app.files.get("index.html", ""), - input_data=body.input_data, - backend_result=None, error=validation_err, - ).model_dump() backend_code = app.files.get("backend.py") backend_result = None @@ -215,7 +199,7 @@ async def execute_app(body: AppExecute): error = None if backend_code: try: - exec_result = await execute_backend_code(backend_code, body.input_data) + exec_result = await execute_backend_code(backend_code) backend_result = exec_result.result stdout_text = exec_result.stdout stderr_text = exec_result.stderr @@ -225,7 +209,6 @@ async def execute_app(body: AppExecute): return AppExecuteResult( app_id=app.id, app_name=app.name, frontend_code=app.files.get("index.html", ""), - input_data=body.input_data, backend_result=backend_result, stdout=stdout_text, stderr=stderr_text, error=error, ).model_dump() diff --git a/backend/apps/app_builder/app_builder_skill.md b/backend/apps/app_builder/app_builder_skill.md index 4441f3f4..38390057 100644 --- a/backend/apps/app_builder/app_builder_skill.md +++ b/backend/apps/app_builder/app_builder_skill.md @@ -12,7 +12,6 @@ here is served directly to the live preview. |------|----------|---------| | `index.html` | **Yes** | Entry point. Must be a complete HTML document. This is the ONLY file the preview iframe loads — never rename it. | | `meta.json` | **Yes** | `{"name":"…","description":"…"}` — displayed in the UI header. Always write this. | -| `schema.json` | Recommended | JSON Schema defining the input form (the "Test Input" tab). | | `backend.py` | Optional | Server-side Python executed before rendering. | | Everything else | Optional | JS, CSS, images, subdirectories — referenced from `index.html` via relative paths. | @@ -20,77 +19,24 @@ here is served directly to the live preview. - Name the main HTML file anything other than `index.html` — the platform will not find it and the preview will be blank. -- Use `document.write()` — it breaks the injected data globals. - Assume any external server or API is available unless the user provides one. --- -## Injected globals - -Before `index.html` loads, the platform injects two globals: - -```javascript -window.APP_BUILDER_INPUT // Object — structured input from the schema form -window.APP_BUILDER_BACKEND_RESULT // Object | null — result from backend.py execution -``` - -These are available immediately in any ` ``` diff --git a/backend/apps/app_builder/executor.py b/backend/apps/app_builder/executor.py index 2ce9bd87..739a4706 100644 --- a/backend/apps/app_builder/executor.py +++ b/backend/apps/app_builder/executor.py @@ -16,12 +16,12 @@ class BackendExecResult: stderr: str -async def execute_backend_code(code: str, input_data: dict) -> BackendExecResult: +async def execute_backend_code(code: str) -> BackendExecResult: """Execute user-provided Python code in a subprocess. - The code receives ``input_data`` as a global dict and must assign its - result to a global ``result`` dict. User print() calls are captured - separately from the result via an in-process StringIO redirect. + The code must assign its result to a global ``result`` dict. + User print() calls are captured separately from the result via + an in-process StringIO redirect. """ preamble = ( @@ -29,7 +29,6 @@ async def execute_backend_code(code: str, input_data: dict) -> BackendExecResult "_orig_stdout = sys.stdout\n" "_capture = io.StringIO()\n" "sys.stdout = _capture\n" - "input_data = json.loads(sys.stdin.read())\n" "result = {}\n" ) postamble = ( @@ -40,14 +39,13 @@ async def execute_backend_code(code: str, input_data: dict) -> BackendExecResult proc = await asyncio.create_subprocess_exec( sys.executable, "-c", wrapper, - stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) try: stdout, stderr = await asyncio.wait_for( - proc.communicate(input=json.dumps(input_data).encode()), + proc.communicate(), timeout=TIMEOUT_SECONDS, ) except asyncio.TimeoutError: diff --git a/backend/apps/app_builder/helpers.py b/backend/apps/app_builder/helpers.py index 24b07e31..743070a4 100644 --- a/backend/apps/app_builder/helpers.py +++ b/backend/apps/app_builder/helpers.py @@ -1,60 +1,9 @@ -"""Pure helpers for data injection, validation, and directory walking.""" +"""Pure helpers for the app builder.""" from __future__ import annotations -import base64 -import json import os -from jsonschema import validate as schema_validate, ValidationError as SchemaValidationError - - -def validate_against_schema(data: dict, schema: dict) -> str | None: - """Validate *data* against *schema*. Return an error string or None.""" - try: - schema_validate(instance=data, schema=schema) - return None - except SchemaValidationError as exc: - path = " -> ".join(str(p) for p in exc.absolute_path) if exc.absolute_path else "(root)" - return f"Schema validation failed at {path}: {exc.message}" - - -def build_data_injection(input_json: str, result_json: str) -> str: - return ( - "" - ) - - -def inject_data_into_html(html: str, input_json: str = "{}", result_json: str = "null") -> str: - injection = build_data_injection(input_json, result_json) - if "" in html: - return html.replace("", f"{injection}\n", 1) - if " tuple[str, str]: - try: - decoded = json.loads(base64.b64decode(d)) - input_json = json.dumps(decoded.get("i", {})) - result_json = json.dumps(decoded.get("r", None)) - return input_json, result_json - except Exception: - return "{}", "null" - def walk_directory(folder: str) -> dict[str, str]: files: dict[str, str] = {} diff --git a/backend/apps/app_builder/templates.py b/backend/apps/app_builder/templates.py index c228b0bb..2eb288b9 100644 --- a/backend/apps/app_builder/templates.py +++ b/backend/apps/app_builder/templates.py @@ -45,21 +45,11 @@ APP_BUILDER_TEMPLATE_INDEX = """\

Describe what you want to build and the agent will update this app.

""" -APP_BUILDER_TEMPLATE_SCHEMA = """\ -{ - "type": "object", - "properties": {}, - "required": [] -} -""" - APP_BUILDER_TEMPLATE_META = """\ { "name": "", @@ -69,6 +59,5 @@ APP_BUILDER_TEMPLATE_META = """\ APP_BUILDER_TEMPLATE_FILES = { "index.html": APP_BUILDER_TEMPLATE_INDEX, - "schema.json": APP_BUILDER_TEMPLATE_SCHEMA, "meta.json": APP_BUILDER_TEMPLATE_META, }