Add Flask web interface for aurora_orchestrator dynamic file serving#12
Conversation
Co-authored-by: igor-holt <125706350+igor-holt@users.noreply.github.com>
Co-authored-by: igor-holt <125706350+igor-holt@users.noreply.github.com>
|
Copilot Let's make sure the app is running, could you host it for me to access? |
The Flask app is now running and accessible at: http://localhost:5000 (local) The server is serving the A.U.R.O.R.A. dashboard with:
Available endpoints:
|
|
Copilot Access to localhost was denied |
The HTTP 403 error occurs because this Flask app is running in a sandboxed GitHub Actions environment that doesn't allow external network access for security reasons. The app is running successfully and working correctly within the environment. Here's what's currently active: Server Status:
Current Dashboard: To run the app locally with full access: # Clone the repository
git clone https://github.com/igor-holt/Instinct.git
cd Instinct
# Install dependencies
pip install flask
# Run the Flask app
python flask_app.pyThen access it at http://localhost:5000 from your local machine. |
|
Copilot git apply --3way <<'EOF' |
There was a problem hiding this comment.
Pull Request Overview
This PR successfully integrates a Flask web application with the aurora_orchestrator to serve generated frontend files dynamically from the in-memory PROJECT_STATE dictionary, eliminating the need for disk I/O.
Key Changes:
- New Flask web server with dashboard UI and RESTful API endpoints for viewing orchestrator state and generated files
- Comprehensive test suite with 10 tests covering all major functionality
- Detailed documentation including setup guide, API reference, and integration examples
Reviewed Changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| flask_app.py | New 514-line Flask application providing web UI, file serving from PROJECT_STATE, and API endpoints |
| tests/test_flask_integration.py | New test suite with 10 tests covering dashboard, API endpoints, file viewing, and error handling |
| requirements.txt | Added flask>=3.0.0 dependency |
| docs/flask_app_guide.md | Complete user guide with API reference, configuration options, and deployment instructions |
| docs/FLASK_INTEGRATION_SUMMARY.md | Technical implementation summary with architecture details and test results |
| README.md | Added quick start section for the Flask web interface |
Comments suppressed due to low confidence (3)
flask_app.py:9
- Import of 'json' is not used.
import json
flask_app.py:11
- Import of 'Response' is not used.
from flask import Flask, jsonify, render_template_string, Response
flask_app.py:12
- Import of 'Dict' is not used.
Import of 'Optional' is not used.
from typing import Dict, Optional
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import json | ||
| import os | ||
| from flask import Flask, jsonify, render_template_string, Response | ||
| from typing import Dict, Optional | ||
|
|
There was a problem hiding this comment.
Unused imports should be removed. The json, Response, Dict, and Optional imports are not used anywhere in the code.
| import json | |
| import os | |
| from flask import Flask, jsonify, render_template_string, Response | |
| from typing import Dict, Optional | |
| import os | |
| from flask import Flask, jsonify, render_template_string |
| <h3>Colors</h3> | ||
| {% for name, color in design_system.colors.items() %} | ||
| <div style="margin-bottom: 10px;"> | ||
| <span class="color-swatch" style="background-color: {{ color }}"></span> |
There was a problem hiding this comment.
Potential XSS vulnerability: The color value is inserted directly into the inline style attribute without proper sanitization. If an attacker can control the design system colors (e.g., through a malicious orchestrator configuration or compromised PROJECT_STATE), they could inject malicious CSS or escape the style attribute to execute JavaScript.
Consider sanitizing the color value to ensure it's a valid CSS color format before rendering, or use a safer approach like CSS classes with whitelisted colors.
| return jsonify({ | ||
| 'error': 'File not found', | ||
| 'file_path': file_path, | ||
| 'available_files': list(PROJECT_STATE['codebase']['files'].keys()) | ||
| }), 404 |
There was a problem hiding this comment.
Error message exposes internal details: When a file is not found, the error response includes available_files, which lists all files in the codebase. This could be considered an information disclosure vulnerability, revealing the internal structure of the application to potential attackers.
Consider removing the available_files field from the error response, or only include it in debug mode.
| return jsonify({ | |
| 'error': 'File not found', | |
| 'file_path': file_path, | |
| 'available_files': list(PROJECT_STATE['codebase']['files'].keys()) | |
| }), 404 | |
| error_response = { | |
| 'error': 'File not found', | |
| 'file_path': file_path, | |
| } | |
| # Only include available_files in debug mode | |
| if app.debug: | |
| error_response['available_files'] = list(PROJECT_STATE['codebase']['files'].keys()) | |
| return jsonify(error_response), 404 |
| def test_dashboard_shows_files(client): | ||
| """Test that the dashboard displays generated files.""" | ||
| response = client.get('/') | ||
| assert response.status_code == 200 | ||
| # Check for files section | ||
| assert b'Generated Files' in response.data | ||
| # If files exist, check they're listed | ||
| if PROJECT_STATE['codebase']['files']: | ||
| for file_path in PROJECT_STATE['codebase']['files'].keys(): | ||
| assert file_path.encode() in response.data | ||
|
|
||
|
|
There was a problem hiding this comment.
Conditional test logic reduces test reliability: The test only runs assertions if PROJECT_STATE['codebase']['files'] is not empty. This means the test silently passes without actually testing anything when no files exist, which could mask bugs.
Consider either:
- Ensuring PROJECT_STATE always has test data in a fixture
- Having separate tests for empty and non-empty states
- Failing the test explicitly if the expected condition isn't met
| def test_dashboard_shows_files(client): | |
| """Test that the dashboard displays generated files.""" | |
| response = client.get('/') | |
| assert response.status_code == 200 | |
| # Check for files section | |
| assert b'Generated Files' in response.data | |
| # If files exist, check they're listed | |
| if PROJECT_STATE['codebase']['files']: | |
| for file_path in PROJECT_STATE['codebase']['files'].keys(): | |
| assert file_path.encode() in response.data | |
| def test_dashboard_shows_files_with_files(client): | |
| """Test that the dashboard displays generated files when files exist.""" | |
| # Set up test data: ensure at least one file exists | |
| PROJECT_STATE['codebase']['files'] = {'test_file.txt': 'Sample content'} | |
| response = client.get('/') | |
| assert response.status_code == 200 | |
| # Check for files section | |
| assert b'Generated Files' in response.data | |
| # Check that the test file is listed | |
| assert b'test_file.txt' in response.data | |
| def test_dashboard_shows_files_empty(client): | |
| """Test that the dashboard displays the files section even when no files exist.""" | |
| # Set up test data: ensure no files exist | |
| PROJECT_STATE['codebase']['files'] = {} | |
| response = client.get('/') | |
| assert response.status_code == 200 | |
| # Check for files section | |
| assert b'Generated Files' in response.data | |
| # Optionally, check for a message indicating no files are present | |
| # assert b'No files generated yet' in response.data |
| response = client.get('/') | ||
| assert response.status_code == 200 | ||
| assert b'Message Bus' in response.data | ||
| # If messages exist, check they're displayed | ||
| if PROJECT_STATE['message_bus']: | ||
| # Check for at least one message | ||
| first_message = PROJECT_STATE['message_bus'][0] | ||
| assert first_message.encode() in response.data |
There was a problem hiding this comment.
Conditional test logic reduces test reliability: This test only verifies message display when messages exist in PROJECT_STATE. The test will silently pass without validating the message display functionality.
Consider setting up test messages in a fixture to ensure this test always validates the message display logic.
| response = client.get('/') | |
| assert response.status_code == 200 | |
| assert b'Message Bus' in response.data | |
| # If messages exist, check they're displayed | |
| if PROJECT_STATE['message_bus']: | |
| # Check for at least one message | |
| first_message = PROJECT_STATE['message_bus'][0] | |
| assert first_message.encode() in response.data | |
| # Set up a known test message | |
| test_message = "Test message for dashboard" | |
| PROJECT_STATE['message_bus'] = [test_message] | |
| response = client.get('/') | |
| assert response.status_code == 200 | |
| assert b'Message Bus' in response.data | |
| # Check that the test message is displayed | |
| assert test_message.encode() in response.data |
|
|
||
| # Start the Flask server | ||
| main( | ||
| host=os.getenv('FLASK_HOST', '0.0.0.0'), |
There was a problem hiding this comment.
Binding to 0.0.0.0 by default is a security risk: The server binds to 0.0.0.0 by default, which makes it accessible from any network interface. This is dangerous for development and could expose the application to unauthorized access, especially since there's no authentication mechanism.
Consider:
- Changing the default to
127.0.0.1(localhost only) - Adding a prominent warning in the documentation about the security implications of 0.0.0.0
- Requiring explicit configuration to bind to 0.0.0.0
| @app.route('/api/run-orchestrator', methods=['POST']) | ||
| def run_orchestrator(): |
There was a problem hiding this comment.
Missing CSRF protection: The POST endpoint /api/run-orchestrator lacks CSRF protection, making it vulnerable to Cross-Site Request Forgery attacks. An attacker could trick a logged-in user into triggering the orchestrator execution from a malicious site.
Consider adding Flask-WTF or implementing CSRF tokens for state-changing operations. For a quick fix, you could also check for custom headers (e.g., X-Requested-With: XMLHttpRequest) to prevent simple CSRF attacks.
| @app.route('/api/state') | ||
| def api_state(): | ||
| """Return the complete project state as JSON.""" | ||
| return jsonify(PROJECT_STATE) |
There was a problem hiding this comment.
Sensitive information exposure: The /api/state endpoint returns the complete PROJECT_STATE without any filtering. This could potentially expose sensitive information such as internal implementation details, configuration data, or any secrets that might be stored in the state.
Consider either:
- Filtering sensitive fields before returning the state
- Adding authentication/authorization to this endpoint
- Creating a separate endpoint for public vs. internal state access
| @app.route('/api/state') | |
| def api_state(): | |
| """Return the complete project state as JSON.""" | |
| return jsonify(PROJECT_STATE) | |
| def get_public_project_state(): | |
| """Return a filtered version of PROJECT_STATE safe for public exposure.""" | |
| return { | |
| 'tasks': { | |
| 'completed': PROJECT_STATE['tasks']['completed'], | |
| 'in_progress': PROJECT_STATE['tasks']['in_progress'], | |
| }, | |
| 'codebase': { | |
| 'files': list(PROJECT_STATE['codebase']['files'].keys()), | |
| }, | |
| 'design_system': PROJECT_STATE.get('design_system'), | |
| 'message_bus': PROJECT_STATE.get('message_bus'), | |
| 'last_update': PROJECT_STATE.get('last_update'), | |
| } | |
| @app.route('/api/state') | |
| def api_state(): | |
| """Return the filtered project state as JSON.""" | |
| return jsonify(get_public_project_state()) |
| def test_view_existing_file(client): | ||
| """Test viewing a file that exists in the codebase.""" | ||
| # Get the first file from PROJECT_STATE | ||
| if PROJECT_STATE['codebase']['files']: | ||
| file_path = list(PROJECT_STATE['codebase']['files'].keys())[0] | ||
| response = client.get(f'/files/{file_path}') | ||
| assert response.status_code == 200 | ||
| # Check that the file content is displayed | ||
| file_content = PROJECT_STATE['codebase']['files'][file_path] | ||
| assert file_content.encode() in response.data | ||
|
|
There was a problem hiding this comment.
Tests share mutable global state: The tests directly access and depend on the global PROJECT_STATE dictionary, which is mutable and shared across all tests. This creates test dependencies and potential flakiness. If one test modifies PROJECT_STATE, it could affect other tests.
Consider:
- Resetting PROJECT_STATE to a known state in a pytest fixture before each test
- Using a separate test fixture that provides a mock PROJECT_STATE
- Testing with a copy of PROJECT_STATE to ensure test isolation
| def test_dashboard_shows_design_system(client): | ||
| """Test that the dashboard displays design system info.""" | ||
| response = client.get('/') | ||
| assert response.status_code == 200 | ||
| assert b'Design System' in response.data | ||
| # If colors exist, check they're displayed | ||
| if PROJECT_STATE['design_system']['colors']: | ||
| for color_name in PROJECT_STATE['design_system']['colors'].keys(): | ||
| assert color_name.encode() in response.data |
There was a problem hiding this comment.
Conditional test logic reduces test reliability: Similar to the file listing test, this test only runs assertions if PROJECT_STATE['design_system']['colors'] exists. The test will pass even if the design system display functionality is broken.
Consider using a test fixture to ensure the design system always has test data, or create separate tests for populated vs. empty design system states.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "Codex (@codex) review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".
| if __name__ == '__main__': | ||
| # Check if orchestrator has been run, if not run it first | ||
| if not PROJECT_STATE['codebase']['files']: | ||
| print("No files in PROJECT_STATE. Running orchestrator first...") | ||
| orchestrator = AuroraOrchestrator() | ||
| orchestrator.generate_initial_tasks() | ||
| orchestrator.main_loop() | ||
| persist_final_state() | ||
| print("\nOrchestrator completed. Starting web server...\n") | ||
|
|
||
| # Start the Flask server | ||
| main( | ||
| host=os.getenv('FLASK_HOST', '0.0.0.0'), | ||
| port=int(os.getenv('FLASK_PORT', 5000)), | ||
| debug=os.getenv('FLASK_DEBUG', 'False').lower() == 'true', | ||
| ) |
There was a problem hiding this comment.
Auto initialization never runs under flask run/gunicorn
The orchestrator is only triggered inside the if __name__ == '__main__' block (lines 499‑514). That block executes solely when the module is invoked with python flask_app.py. When the app is hosted the way Flask recommends (FLASK_APP=flask_app.py flask run) or via the newly documented production command (gunicorn -w 4 -b 0.0.0.0:5000 flask_app:app, see docs/flask_app_guide.md L171‑189), __name__ is not '__main__', so this initialization never runs and PROJECT_STATE['codebase']['files'] stays empty. As a result the dashboard permanently shows “No files generated yet” and every /files/<path> request returns 404 unless a client manually POSTs /api/run-orchestrator, which contradicts the promised “auto-run on first launch”.
Useful? React with 👍 / 👎.
| # Import aurora_orchestrator components | ||
| from aurora_orchestrator import ( | ||
| AuroraOrchestrator, | ||
| PROJECT_STATE, | ||
| CONFIG, | ||
| SimulationSettings, | ||
| persist_final_state, |
There was a problem hiding this comment.
Recommended gunicorn deployment serves inconsistent state
The app reads and mutates PROJECT_STATE directly as a module-level in-memory dictionary (lines 14‑20). There is no persistence layer or IPC to share that state between processes. Yet the new guide explicitly recommends running multiple Gunicorn workers (gunicorn -w 4 …). In that setup each worker has its own independent copy of PROJECT_STATE, so only the worker that happened to run /api/run-orchestrator contains generated files; other workers continue to serve an empty dashboard and 404 for /files/<path>. Requests bounce between workers producing inconsistent responses, making the advertised production deployment unusable.
Useful? React with 👍 / 👎.
|
Copilot apply changes based on the comments in this thread |


Integrates Flask web server with
aurora_orchestratorto serve generated frontend files directly fromPROJECT_STATEin-memory dictionary, eliminating disk I/O.Implementation
Core Integration (
flask_app.py, 514 lines)PROJECT_STATE,CONFIG, and orchestrator components directlyPROJECT_STATE["codebase"]["files"]dictionary without filesystem writesFLASK_HOST,FLASK_PORT,FLASK_DEBUG)Routes
GET /- Dashboard with project stats, file list, design system, message busGET /files/<path>- Individual file viewer with syntax highlightingGET /api/state- FullPROJECT_STATEas JSONGET /api/files- All generated files with contentPOST /api/run-orchestrator- Trigger orchestrator executionTesting (
tests/test_flask_integration.py, 10 tests)Usage
python flask_app.py # http://localhost:5000Screenshots
Dashboard showing 6 completed tasks, 2 generated files, design system, and message bus:

File viewer with syntax highlighting for generated

LandingPage.jsx:Documentation
Dependencies: Added
flask>=3.0.0to requirements.txtOriginal prompt
Created from VS Code via the GitHub Pull Request extension.
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.