Replaces the separate Vue/Vite `console/` SPA with a same-origin, server-rendered console built on a module-level Jinja2 Environment with select_autoescape(["html","xml"]). - Add api/console_web.py with /ui/ routes (dashboard, tasks, task detail/timeline, config) and a _status_fragment polled every 10s. - Refactor api/console.py into a typed ConsoleService shared by the JSON and HTML routers so validation/persistence cannot drift. - Remove RUNTIME_CONSOLE_STATIC_DIR, SpaStaticFiles, and the wildcard CORS middleware from api/rest.py; GET / now redirects to /ui/. - Delete the top-level console/ project; add jinja2 and python-multipart as direct dependencies and ship templates/CSS/JS via package-data. - Add 31 tests (XSS probes, PRG flows, fragment refresh, no-static-dir and no-CORS regressions, wheel-packaging smoke test). /console/* JSON endpoints remain unchanged. The console keeps the trusted-network-only boundary; auth/CSRF is intentionally deferred. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.8 KiB
ADDED Requirements
Requirement: Runtime console pages SHALL be served by the Runtime FastAPI application
The Runtime FastAPI application SHALL serve its operator console from
same-origin /ui/ routes without requiring a separately running frontend
process, a prebuilt SPA directory, or RUNTIME_CONSOLE_STATIC_DIR. GET /
SHALL redirect an operator to /ui/.
Scenario: Open the built-in console without static-directory configuration
- WHEN the Runtime application starts without
RUNTIME_CONSOLE_STATIC_DIR - THEN
GET /redirects to/ui/andGET /ui/returns an HTML dashboard rendered by the Runtime process
Scenario: Navigate the operator workflows through page routes
- WHEN an operator opens
/ui/tasks,/ui/tasks/{task_id}, or/ui/config - THEN the Runtime returns HTML pages for task browsing, task timeline detail, and device/runtime configuration respectively
Requirement: Runtime console HTML SHALL use one autoescaping template environment
Every Runtime console HTML response SHALL be rendered through one
process-wide Jinja2 environment configured to autoescape .html and .xml
templates. Page handlers SHALL NOT construct HTML through f-strings, string
concatenation, or post-process rendered output to bypass that environment.
Scenario: Untrusted device and task values are rendered safely
- WHEN a device name, task goal, failure reason, or timeline value contains
<script>alert(1)</script> - THEN the rendered console HTML contains an escaped text representation and contains no script element originating from that value
Scenario: A future page inherits HTML autoescaping
- WHEN a future Runtime console route renders a
.htmltemplate through the shared environment - THEN its interpolated values are HTML-escaped without route-specific escaping configuration
Requirement: Runtime console pages SHALL preserve console operational workflows
The server-rendered console SHALL let an operator inspect device status, list
and filter tasks, inspect a task's ordered timeline including available
screenshots, register or remove a supported device, and view or update
max_steps. Successful configuration mutations SHALL use POST/Redirect/GET;
invalid form input SHALL be re-rendered as a readable HTML error without
applying a partial mutation.
Scenario: Browse filtered tasks and inspect a timeline
- WHEN an operator selects a device or status filter and opens a known task
- THEN the task list contains only matching tasks and the task page renders its timeline in step order with its screenshot when one exists
Scenario: Register a device from the configuration page
- WHEN an operator submits valid supported-device form values
- THEN the Runtime registers and persists the device, responds with a redirect to the configuration page, and the device is visible after the redirect
Scenario: Reject invalid configuration without partial mutation
- WHEN an operator submits an unsupported driver type, malformed
connection value, or non-positive
max_steps - THEN the Runtime returns an HTML validation error, preserves the prior Runtime/configuration state, and does not perform a redirect
Requirement: Dashboard live status SHALL remain server-rendered
The dashboard SHALL retain periodic live-status refresh without restoring a client-side application framework. Its browser enhancement SHALL request a server-rendered HTML fragment and replace only the live-status region; it SHALL NOT rebuild console state from a JSON API response.
Scenario: Refresh dashboard status after the polling interval
- WHEN the dashboard refresh enhancement runs while the Runtime is available
- THEN it retrieves a Jinja2-rendered status fragment and updates the dashboard's live-status region without a full-page reload
Requirement: Existing console JSON API SHALL remain compatible
The Runtime SHALL continue to expose the existing /console/devices,
/console/tasks, /console/tasks/{task_id}, /console/tasks/{task_id}/timeline,
and /console/config JSON endpoints with their existing methods, status
codes, payloads, filtering behavior, and persistence semantics. Page and JSON
routes SHALL use the same API-local console operations rather than issuing
HTTP requests to each other.
Scenario: Programmatic client reads console data after the UI migration
- WHEN a client calls an existing
GET /console/*endpoint after the server-rendered console is deployed - THEN it receives the same JSON response shape and status behavior as before the migration
Scenario: A page mutation is visible through the JSON API
- WHEN an operator registers or removes a device or updates
max_stepsthrough a/ui/form - THEN the corresponding
/console/*JSON endpoint reports the same resulting Runtime state
Requirement: Runtime console templates and assets SHALL ship with the Python package
The Runtime distribution SHALL package all console templates and static assets
needed by /ui/. The Runtime console SHALL not depend on the top-level Vue/Vite
console/ project, Node package installation, Vite configuration, or the
SPA-only static-directory mount. Same-origin console operation SHALL not
require wildcard CORS configured for Vite development.
Scenario: Run the console from an installed Runtime wheel
- WHEN
device-agent-runtimeis built and installed outside the source checkout - THEN the Runtime can serve
/ui/and its required CSS/browser assets from packaged resources
Scenario: Start a Runtime after the SPA workflow is removed
- WHEN an operator starts the Runtime API using the documented Python command
- THEN the console is available at
/ui/withoutnpm install,npm run build,VITE_API_BASE_URL, orRUNTIME_CONSOLE_STATIC_DIR