Skip to content

Serve bundled agent-canvas frontend from agent-server#3676

Draft
enyst wants to merge 4 commits into
mainfrom
openhands/agent-canvas-frontend
Draft

Serve bundled agent-canvas frontend from agent-server#3676
enyst wants to merge 4 commits into
mainfrom
openhands/agent-canvas-frontend

Conversation

@enyst

@enyst enyst commented Jun 11, 2026

Copy link
Copy Markdown
Member

HUMAN:

  • A human has tested these changes.

AGENT:


Why

Users who clone software-agent-sdk should be able to run the same agent-canvas stack from this repository after make build, without manually installing the npm package separately or changing agent-canvas's process model. The npm @openhands/agent-canvas CLI runs separate agent-server, automation backend, static frontend, and ingress processes, so this PR now reuses that launcher instead of serving frontend assets from FastAPI.

Summary

  • make build now also downloads pinned prebuilt @openhands/agent-canvas@1.0.0-rc.7 into agent-canvas/.
  • Added make run to launch that package with OH_AGENT_SERVER_LOCAL_PATH pointed at this checkout, so agent-canvas starts the local SDK's agent-server plus the automation backend, static frontend server, and ingress router.
  • Preserved agent-canvas CLI modes such as --frontend-only, --backend-only, and --port through make run ARGS="..." instead of duplicating its routing/static-server behavior in agent-server.
  • make canvas remains as an alias for make run.

Issue Number

Closes #3658.

How to Test

Commands run by OpenHands:

make build

Result: completed successfully; uv sync --dev was up to date, pre-commit hooks were installed, and the prebuilt agent-canvas package was downloaded to agent-canvas/.

rm -rf frontend agent-canvas
make agent-canvas-frontend
test -f agent-canvas/bin/agent-canvas.mjs
test -f agent-canvas/config/defaults.json
node agent-canvas/bin/agent-canvas.mjs --version

Result: completed successfully and printed 1.0.0-rc.7, confirming the Makefile fetches the pinned agent-canvas package into the package-root-shaped agent-canvas/ directory.

make run ARGS="--info"

Result: completed successfully and printed @openhands/agent-canvas 1.0.0-rc.7 with the default stack versions and ports, confirming the local launcher is using the pinned downloaded package.

make run ARGS="--help" | grep -E -- '--frontend-only|--backend-only|automation backend'

Result: completed successfully and confirmed the launcher exposes the current agent-canvas modes, including --frontend-only, --backend-only, and the automation backend stack behavior.

uv run pre-commit run --files .gitignore Makefile .github/workflows/pypi-release.yml MANIFEST.in openhands-agent-server/openhands/agent_server/agent-server.spec openhands-agent-server/openhands/agent_server/api.py openhands-agent-server/pyproject.toml tests/agent_server/test_api.py

Result: all hooks passed, including ruff format/lint, pycodestyle, pyright, import dependency rules, and tool subclass registration.

uv run pytest tests/agent_server/test_api.py -q

Result: 41 passed, 5 warnings in 4.08s, confirming the previous direct FastAPI static-serving changes were removed without regressing existing agent-server API tests.

Video/Screenshots

No screenshot was captured. The relevant end-to-end smoke check here is make run ARGS="--info" / --help, which executes the downloaded agent-canvas CLI from agent-canvas and verifies the exposed modes. A human should still run the full interactive UI flow before marking this ready for review.

Type

  • Bug fix
  • Feature
  • Refactor
  • Breaking change
  • Docs / chore

Notes

The downloaded agent-canvas/ directory is intentionally ignored in git. This PR no longer packages frontend assets into openhands-agent-server or serves them from FastAPI; it keeps agent-canvas's existing separated static frontend, ingress, agent-server, and automation backend process model.

This PR description was updated by an AI agent (OpenHands) on behalf of the requester.


Agent Server images for this PR

GHCR package: https://github.com/OpenHands/agent-sdk/pkgs/container/agent-server

Variants & Base Images

Variant Architectures Base Image Docs / Tags
java amd64, arm64 eclipse-temurin:17-jdk Link
python amd64, arm64 nikolaik/python-nodejs:python3.13-nodejs22-slim Link
golang amd64, arm64 golang:1.21-bookworm Link

Pull (multi-arch manifest)

# Each variant is a multi-arch manifest supporting both amd64 and arm64
docker pull ghcr.io/openhands/agent-server:fdee1b1-python

Run

docker run -it --rm \
  -p 8000:8000 \
  --name agent-server-fdee1b1-python \
  ghcr.io/openhands/agent-server:fdee1b1-python

All tags pushed for this build

ghcr.io/openhands/agent-server:fdee1b1-golang-amd64
ghcr.io/openhands/agent-server:fdee1b18fc555541918df7e4b3ef03f0bab7d266-golang-amd64
ghcr.io/openhands/agent-server:openhands-agent-canvas-frontend-golang-amd64
ghcr.io/openhands/agent-server:fdee1b1-golang_tag_1.21-bookworm-amd64
ghcr.io/openhands/agent-server:fdee1b1-golang-arm64
ghcr.io/openhands/agent-server:fdee1b18fc555541918df7e4b3ef03f0bab7d266-golang-arm64
ghcr.io/openhands/agent-server:openhands-agent-canvas-frontend-golang-arm64
ghcr.io/openhands/agent-server:fdee1b1-golang_tag_1.21-bookworm-arm64
ghcr.io/openhands/agent-server:fdee1b1-java-amd64
ghcr.io/openhands/agent-server:fdee1b18fc555541918df7e4b3ef03f0bab7d266-java-amd64
ghcr.io/openhands/agent-server:openhands-agent-canvas-frontend-java-amd64
ghcr.io/openhands/agent-server:fdee1b1-eclipse-temurin_tag_17-jdk-amd64
ghcr.io/openhands/agent-server:fdee1b1-java-arm64
ghcr.io/openhands/agent-server:fdee1b18fc555541918df7e4b3ef03f0bab7d266-java-arm64
ghcr.io/openhands/agent-server:openhands-agent-canvas-frontend-java-arm64
ghcr.io/openhands/agent-server:fdee1b1-eclipse-temurin_tag_17-jdk-arm64
ghcr.io/openhands/agent-server:fdee1b1-python-amd64
ghcr.io/openhands/agent-server:fdee1b18fc555541918df7e4b3ef03f0bab7d266-python-amd64
ghcr.io/openhands/agent-server:openhands-agent-canvas-frontend-python-amd64
ghcr.io/openhands/agent-server:fdee1b1-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-slim-amd64
ghcr.io/openhands/agent-server:fdee1b1-python-arm64
ghcr.io/openhands/agent-server:fdee1b18fc555541918df7e4b3ef03f0bab7d266-python-arm64
ghcr.io/openhands/agent-server:openhands-agent-canvas-frontend-python-arm64
ghcr.io/openhands/agent-server:fdee1b1-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-slim-arm64
ghcr.io/openhands/agent-server:fdee1b1-golang
ghcr.io/openhands/agent-server:fdee1b18fc555541918df7e4b3ef03f0bab7d266-golang
ghcr.io/openhands/agent-server:openhands-agent-canvas-frontend-golang
ghcr.io/openhands/agent-server:fdee1b1-golang_tag_1.21-bookworm
ghcr.io/openhands/agent-server:fdee1b1-java
ghcr.io/openhands/agent-server:fdee1b18fc555541918df7e4b3ef03f0bab7d266-java
ghcr.io/openhands/agent-server:openhands-agent-canvas-frontend-java
ghcr.io/openhands/agent-server:fdee1b1-eclipse-temurin_tag_17-jdk
ghcr.io/openhands/agent-server:fdee1b1-python
ghcr.io/openhands/agent-server:fdee1b18fc555541918df7e4b3ef03f0bab7d266-python
ghcr.io/openhands/agent-server:openhands-agent-canvas-frontend-python
ghcr.io/openhands/agent-server:fdee1b1-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-slim

About Multi-Architecture Support

  • Each variant tag (e.g., fdee1b1-python) is a multi-arch manifest supporting both amd64 and arm64
  • Docker automatically pulls the correct architecture for your platform
  • Individual architecture tags (e.g., fdee1b1-python-amd64) are also available if needed

Co-authored-by: openhands <openhands@all-hands.dev>
@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Python API breakage checks — ✅ PASSED

Result:PASSED

Action log

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

REST API breakage checks (OpenAPI) — ✅ PASSED

Result:PASSED

Action log

@github-actions

Copy link
Copy Markdown
Contributor

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands-agent-server/openhands/agent_server
   api.py2902292%119, 121–126, 128, 130, 132, 166, 178, 193, 199, 401, 511, 514, 518–520, 522, 529
TOTAL309141347456% 

@all-hands-bot all-hands-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ QA Report: PASS WITH ISSUES

Functional behavior passes: the bundled agent-canvas frontend served from agent-server root in source, wheel/sdist, and PyInstaller binary runs; the remaining issue is a failing PR-description CI check.

Does this PR achieve its stated goal?

Yes, functionally. I generated the real @openhands/agent-canvas bundle, ran the agent-server as an actual HTTP service, opened it in a real browser, and confirmed / and SPA routes serve the OpenHands frontend while backend/API routes remain JSON/404 as expected. I also verified configured static_files_path still takes precedence and built distributions/binary serve or contain the bundled frontend assets.

Phase Result
Environment Setup make build completed successfully; no tests/linters were run.
CI Status ⚠️ 22 successful, 7 pending, 1 skipped, 1 failing: PR Description Check.
Functional Verification ✅ Source server, browser UI, static override, wheel/sdist packaging, and PyInstaller runtime behavior verified.
Functional Verification

Test 1: Root and SPA route behavior before/after PR

Step 1 — Establish baseline without the fix:
Checked out origin/main at e3a2a4a2, kept the generated _frontend files present, then ran:

uv run python -m openhands.agent_server --host 127.0.0.1 --port 18101
curl http://127.0.0.1:18101/
curl http://127.0.0.1:18101/settings/llm

Observed:

SERVER_READY_STATUS=200
ROOT_STATUS=200 application/json
ROOT_SNIPPET={"uptime":1.0,"idle_time":1.0,"title":"OpenHands Agent Server",...}
SPA_STATUS=404 application/json
SPA_SNIPPET={"detail":"Not Found"}

This confirms the base server did not serve the bundled frontend from / or use SPA fallback routes.

Step 2 — Apply the PR's changes:
Checked out openhands/agent-canvas-frontend at 8cabec83, ran make agent-canvas-frontend, then started:

uv run python -m openhands.agent_server --host 127.0.0.1 --port 18102

Step 3 — Re-run with the fix in place:
Observed:

SERVER_READY_STATUS=200 application/json
ROOT_STATUS=200 text/html; charset=utf-8
ROOT_SNIPPET=<!DOCTYPE html><html lang="en"><head>...<title>OpenHands</title>...
SPA_STATUS=200 text/html; charset=utf-8
SPA_SAME_AS_ROOT=yes
ASSET_PATH=/assets/react-Dy05vyj5.js
ASSET_STATUS=200 text/javascript; charset=utf-8
API_MISSING_STATUS=404 application/json
API_MISSING_BODY={"detail":"Not Found"}

This shows the PR changes the user-facing behavior as intended: root and SPA paths serve the frontend, static JS assets load, and API paths remain backend-owned instead of falling through to the SPA.

Test 2: Real browser rendering

With the PR server still running, I opened http://127.0.0.1:18102/ in a real browser. The browser landed on http://127.0.0.1:18102/conversations with title OpenHands and rendered the agent-canvas UI (Manage backends, Add Backend). Direct navigation to http://127.0.0.1:18102/settings/llm also loaded with title OpenHands and the same frontend shell, confirming browser-visible SPA fallback behavior.

Test 3: Explicit static_files_path precedence

Step 1 — Establish PR default behavior:
Without static_files_path, the PR server returned bundled frontend HTML from / and /settings/llm as shown above.

Step 2 — Configure explicit static files:
Started the actual ASGI app with Config(static_files_path=Path("/tmp/qa-static")) and a custom /tmp/qa-static/index.html:

uv run python -c 'from pathlib import Path; import uvicorn; from openhands.agent_server.api import create_app; from openhands.agent_server.config import Config; uvicorn.run(create_app(Config(static_files_path=Path("/tmp/qa-static"))), host="127.0.0.1", port=18104)'

Step 3 — Verify override behavior:
Observed:

STATIC_API_READY_STATUS=200 application/json
STATIC_API_ROOT_STATUS=302
STATIC_API_ROOT_LOCATION=/static/index.html
STATIC_API_INDEX_STATUS=200 text/html; charset=utf-8
STATIC_API_INDEX_BODY=<html><body>Configured Static QA</body></html>
STATIC_API_SPA_STATUS=404 application/json
STATIC_API_SPA_BODY={"detail":"Not Found"}

This confirms explicit configured static files still take precedence over the bundled frontend.

Test 4: Packaging and PyInstaller runtime

After make agent-canvas-frontend, I built the agent-server distributions:

uv build --package openhands-agent-server --out-dir /tmp/qa-dist

Observed:

Successfully built /tmp/qa-dist/openhands_agent_server-1.28.0.tar.gz
Successfully built /tmp/qa-dist/openhands_agent_server-1.28.0-py3-none-any.whl
WHEEL_FRONTEND_INDEX=openhands/agent_server/_frontend/index.html
SDIST_FRONTEND_INDEX=openhands_agent_server-1.28.0/openhands/agent_server/_frontend/index.html
SDIST_FRONTEND_ASSET_COUNT=283

Then I built and ran the PyInstaller binary:

uv run pyinstaller openhands-agent-server/openhands/agent_server/agent-server.spec --distpath /tmp/qa-pyi-dist --workpath /tmp/qa-pyi-work --noconfirm --clean
/tmp/qa-pyi-dist/openhands-agent-server --host 127.0.0.1 --port 18105

Observed:

PYINSTALLER_RC=0
PYI_SERVER_READY_STATUS=200 application/json
PYI_ROOT_STATUS=200 text/html; charset=utf-8
PYI_ROOT_SNIPPET=<!DOCTYPE html><html lang="en"><head>...<title>OpenHands</title>...
PYI_SPA_STATUS=200 text/html; charset=utf-8

This confirms release artifacts include the bundle and the PyInstaller binary can serve it at runtime.

Issues Found

  • 🟠 Issue: The PR Description Check is failing. The validator reports: first visible line must be HUMAN:, add a human-written note, keep the unchecked human-tested checkbox, keep AGENT:, and keep the ## Why / ## How to Test sections. Per repo policy, those HUMAN fields are reserved for a human contributor, so I did not edit them.

No inline comments: I did not find any code-tied functional defects during QA.

This QA review was created by an AI agent (OpenHands) on behalf of the requester.

@enyst enyst marked this pull request as draft June 11, 2026 22:55
enyst and others added 3 commits June 11, 2026 23:19
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
@all-hands-bot

Copy link
Copy Markdown
Collaborator

👀 OpenHands is reviewing this pull request.

Conversation: https://nestable-nonremittably-sha.ngrok-free.dev/conversations/6f7e2e74-9581-45d9-a05e-2fb0554548a1


This comment was generated by an AI agent (OpenHands) on behalf of the user.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Start agent-canvas frontend from this repository

2 participants