The Dockerfile copied PRD.md, which is not in the repository, so a clean
clone could not build at all. It also installed node and npm from Ubuntu 24
(node 18) to build a Vite 8 front end that needs node 20.19 or newer, and it
built the interface into a directory nothing served.
The build is now two stages: node:22 builds the interface, the CUDA runtime
image installs the package and carries the built files. The API serves them
when ELY_EYE_UI_DIR points at a directory that exists, and refuses to start
when it is set but empty, naming the build command. The front end already
calls /api/... on its own origin, so same-origin serving needs no client
change.