Files
q792602257andClaude Opus 4.6 e00c50e703 feat(api): server-rendered Jinja2 Runtime console at /ui/
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>
2026-07-15 08:03:13 +08:00

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/ and GET /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 .html template 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_steps through 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-runtime is 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/ without npm install, npm run build, VITE_API_BASE_URL, or RUNTIME_CONSOLE_STATIC_DIR