diff --git a/containers/app/Dockerfile b/containers/app/Dockerfile index 507f45659b01..78faa1215a8d 100644 --- a/containers/app/Dockerfile +++ b/containers/app/Dockerfile @@ -90,7 +90,7 @@ RUN python openhands/core/download.py # No-op to download assets # openhands:openhands -> openhands:app RUN find /app \! -group app -exec chgrp app {} + -COPY --chown=openhands:app --chmod=770 --from=frontend-builder /app/build/client ./frontend/build +COPY --chown=openhands:app --chmod=770 --from=frontend-builder /app/build ./frontend/build COPY --chown=openhands:app --chmod=770 ./containers/app/entrypoint.sh /app/entrypoint.sh USER root diff --git a/frontend/package.json b/frontend/package.json index 6995b97efa55..5fe066c65838 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -46,7 +46,7 @@ "dev": "npm run make-i18n && VITE_MOCK_API=false remix vite:dev", "dev:mock": "npm run make-i18n && VITE_MOCK_API=true remix vite:dev", "build": "npm run make-i18n && tsc && remix vite:build", - "start": "npx sirv-cli build/client/ --single", + "start": "npx sirv-cli build/ --single", "test": "vitest run", "test:coverage": "npm run make-i18n && vitest run --coverage", "dev_wsl": "VITE_WATCH_USE_POLLING=true vite", diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index c5c8c7ebdae6..d9ed9134f8e3 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -8,11 +8,10 @@ import { vitePlugin as remix } from "@remix-run/dev"; export default defineConfig(({ mode }) => { const { - VITE_BACKEND_HOST = "127.0.0.1:3000", - VITE_USE_TLS = "false", - VITE_FRONTEND_PORT = "3001", - VITE_INSECURE_SKIP_VERIFY = "false", - VITE_WATCH_USE_POLLING = "false", + VITE_BACKEND_HOST = "127.0.0.1:3000", + VITE_USE_TLS = "false", + VITE_FRONTEND_PORT = "3001", + VITE_INSECURE_SKIP_VERIFY = "false", } = loadEnv(mode, process.cwd()); const USE_TLS = VITE_USE_TLS === "true"; @@ -23,6 +22,35 @@ export default defineConfig(({ mode }) => { const API_URL = `${PROTOCOL}://${VITE_BACKEND_HOST}/`; const WS_URL = `${WS_PROTOCOL}://${VITE_BACKEND_HOST}/`; const FE_PORT = Number.parseInt(VITE_FRONTEND_PORT, 10); + + /** + * This script is used to unpack the client directory from the frontend build directory. + * Remix SPA mode builds the client directory into the build directory. This function + * moves the contents of the client directory to the build directory and then removes the + * client directory. + * + * This script is used in the buildEnd function of the Vite config. + */ + const unpackClientDirectory = async () => { + const fs = await import("fs"); + const path = await import("path"); + + const buildDir = path.resolve(__dirname, "build"); + const clientDir = path.resolve(buildDir, "client"); + + const files = await fs.promises.readdir(clientDir); + await Promise.all( + files.map((file) => + fs.promises.rename( + path.resolve(clientDir, file), + path.resolve(buildDir, file), + ), + ), + ); + + await fs.promises.rmdir(clientDir); + }; + return { plugins: [ !process.env.VITEST && @@ -33,6 +61,7 @@ export default defineConfig(({ mode }) => { v3_throwAbortReason: true, }, appDirectory: "src", + buildEnd: unpackClientDirectory, ssr: false, }), viteTsconfigPaths(), @@ -67,5 +96,5 @@ export default defineConfig(({ mode }) => { include: ["src/**/*.{ts,tsx}"], }, }, - } + }; }); diff --git a/openhands/server/listen.py b/openhands/server/listen.py index 2f472823672d..32c93a117e23 100644 --- a/openhands/server/listen.py +++ b/openhands/server/listen.py @@ -798,4 +798,4 @@ def github_callback(auth_code: AuthCode): ) -app.mount('/', StaticFiles(directory='./frontend/build/client', html=True), name='dist') +app.mount('/', StaticFiles(directory='./frontend/build', html=True), name='dist')