Skip to content

fix(build): Bundle built WebUI assets within Python wheel#169

Merged
haofeif merged 5 commits into
awslabs:mainfrom
patricka3125:fix/web-ui-packaging
Apr 14, 2026
Merged

fix(build): Bundle built WebUI assets within Python wheel#169
haofeif merged 5 commits into
awslabs:mainfrom
patricka3125:fix/web-ui-packaging

Conversation

@patricka3125

Copy link
Copy Markdown
Collaborator

Problem

Running cao-server in production mode returns 404 at http://localhost:9889/ unless it's launched from inside a cloned copy of the repo via uv run. The REST API works; only the static UI is missing. Users who install CAO via uv tool install git+https://... hit this immediately — the documented "Option B: Production mode" workflow silently fails for them.

Root cause: src/cli_agent_orchestrator/api/main.py resolves the static-file directory with a __file__-relative path walk that only lands on a real web/dist/ when the package is editable-installed inside the original repo:

WEB_DIST = Path(__file__).parent.parent.parent.parent / "web" / "dist"

The hatch wheel config at pyproject.toml only packages src/cli_agent_orchestrator/, so web/dist/ is never copied into the wheel. For any non-editable install, the 4-parent walk lands somewhere inside site-packages/ where no web/dist/ exists, the WEB_DIST.exists() guard returns False, the static mount is silently skipped, and GET / returns 404.

Proposed Solution

Move the built frontend inside the Python package so hatch naturally ships it in the wheel, and resolve the path via importlib.resources so it works identically for editable and wheel installs. Three coordinated changes:

  1. Point Vite's build.outDir at src/cli_agent_orchestrator/web_ui/ so npm run build writes directly into the package tree.
  2. Rewrite the static-dir resolver in api/main.py to anchor on the package via importlib.resources.files("cli_agent_orchestrator") / "web_ui", guarded on index.html existence (graceful no-op if the bundle wasn't built).
  3. Tell hatch to include the (gitignored) built assets in the wheel via [tool.hatch.build].artifacts. No force-include needed — the directory already lives under the packaged tree, so artifacts alone is sufficient to un-exclude the gitignored files.

After these changes, cao-server launched from any cwd — including outside the repo — resolves the UI from inside site-packages/ and serves it. uv tool install users get the Web UI out of the box with no clone required.

Key Changes

  • web/vite.config.ts — add build.outDir: '../src/cli_agent_orchestrator/web_ui' with emptyOutDir: true.
  • src/cli_agent_orchestrator/api/main.py — replace the __file__-relative walk with Path(str(_pkg_files("cli_agent_orchestrator") / "web_ui")); check index.html instead of the directory to avoid false-positives on an empty web_ui/.
  • pyproject.toml — add [tool.hatch.build] artifacts = ["src/cli_agent_orchestrator/web_ui/**"] so hatch ships the built bundle (gitignored) inside the wheel.
  • .gitignore — add src/cli_agent_orchestrator/web_ui/ (build output stays out of git).
  • README.md, web/README.md — update output-path references and drop the obsolete "requires a cloned copy of the repository" note from the production-mode section; add a rebuild snippet (npm run build + uv tool install . --reinstall) for contributors modifying the frontend.

Verification

  • uv build --wheel produces a clean wheel (no duplicate-name warnings) containing cli_agent_orchestrator/web_ui/index.html + the full assets/ subtree — confirmed via unzip -l.
  • uv tool install <wheel> lands assets under ~/.local/share/uv/tools/cli-agent-orchestrator/lib/python*/site-packages/cli_agent_orchestrator/web_ui/.
  • cd /tmp && cao-server --port 9890 (from outside any CAO checkout) returns HTTP 200 at / and serves the bundled index.html — the original 404 is gone. /health continues to return 200, confirming API routes are unaffected.

Follow-up

Maintainers cutting releases should run cd web && npm run build before uv build --wheel to ensure the wheel contains a fresh bundle; a hatch build hook could automate this in a future PR.

patricka3125 and others added 3 commits April 12, 2026 01:06
Move the Vite build output into src/cli_agent_orchestrator/web_ui/ and
resolve it via importlib.resources so cao-server finds the UI regardless
of the invocation cwd or install mode (editable vs. wheel). Previously
the 4-parent __file__ walk only worked for editable installs inside a
cloned repo; uv tool install wheels silently shipped without the UI and
returned 404 at /.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@patricka3125 patricka3125 changed the title fix(build): bundle built UI inside the package so cao-server serves it from any cwd fix(build): bundle built UI within uv package Apr 12, 2026
@patricka3125 patricka3125 changed the title fix(build): bundle built UI within uv package fix(build): Bundle built Web UI within uv package Apr 12, 2026
@patricka3125 patricka3125 changed the title fix(build): Bundle built Web UI within uv package fix(build): Bundle built Web UI within Python wheel Apr 12, 2026
@patricka3125 patricka3125 changed the title fix(build): Bundle built Web UI within Python wheel fix(build): Bundle built WebUI assets within Python wheel Apr 12, 2026
@haofeif haofeif requested a review from abdullahoff April 12, 2026 08:46
@codecov-commenter

codecov-commenter commented Apr 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@76c2fa5). Learn more about missing BASE report.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #169   +/-   ##
=======================================
  Coverage        ?   91.34%           
=======================================
  Files           ?       50           
  Lines           ?     4230           
  Branches        ?        0           
=======================================
  Hits            ?     3864           
  Misses          ?      366           
  Partials        ?        0           
Flag Coverage Δ
unittests 91.34% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@haofeif haofeif requested a review from gutosantos82 April 12, 2026 08:46
@haofeif haofeif added the enhancement New feature or request label Apr 12, 2026
@haofeif

haofeif commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

@abdullahoff @gutosantos82 when you get a chance can you pls help to review this PR? Many thanks.

@abdullahoff abdullahoff left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Tested in a clean Docker container (Python 3.12, Node 20, no repo on disk):

Branch GET / GET /health Wheel has web assets?
main 404 {"detail":"Not Found"} 200 OK ❌ No
pr-169 200 (full React HTML) 200 OK ✅ Yes

What I verified:

  • uv build --wheel on main → unzip -l shows zero web assets in the wheel
  • uv build --wheel on pr-169 → wheel contains cli_agent_orchestrator/web_ui/index.html + assets/*.css
  • assets/*.js
  • Installed via uv tool install, ran cao-server from /tmp/ (outside repo) — frontend loads correctly
  • 844 existing tests pass (1 pre-existing tmux infra failure, unrelated)

The fix is correct:

  • importlib.resources.files() is the right stdlib API for package resource resolution
  • Hatch artifacts directive correctly force-includes gitignored build output into the wheel
  • index.html existence check is better than the old directory-only check

One minor request (non-blocking):
The docs commit removes the "Managed Skills" README section (~32 lines) which is unrelated to this fix.
Consider splitting that into a separate PR or adding a note in the description explaining why it was
removed.

LGTM — nice fix 👍

@abdullahoff

Copy link
Copy Markdown
Contributor

Please rebase before merging

@patricka3125

Copy link
Copy Markdown
Collaborator Author

@abdullahoff feature branch should be up-to-date with main after merge

@haofeif haofeif merged commit 1dfa3fa into awslabs:main Apr 14, 2026
29 checks passed
erikmackinnon pushed a commit to erikmackinnon/cli-agent-orchestrator that referenced this pull request Apr 18, 2026
* fix(packaging): bundle built web UI inside the Python package

Move the Vite build output into src/cli_agent_orchestrator/web_ui/ and
resolve it via importlib.resources so cao-server finds the UI regardless
of the invocation cwd or install mode (editable vs. wheel). Previously
the 4-parent __file__ walk only worked for editable installs inside a
cloned repo; uv tool install wheels silently shipped without the UI and
returned 404 at /.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: Update project root and web/ README

* untrack implementation plan

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: haofeif <56006724+haofeif@users.noreply.github.com>
(cherry picked from commit 1dfa3fa)
haofeif added a commit that referenced this pull request Apr 22, 2026
- Remove "Persistent agent memory system" bullet — PR #179 is still open
  and was not part of this release
- Add missing #106 (stale spinners blocking Claude Code inbox delivery)
- Add missing #135 to cryptography version-bump coverage (46.0.5 → 46.0.7)
- Correct #137 and #157 references to "resolves" — they are issues, not PRs
- Fix install hint in #169 bullet to use real install path
  (uv tool install git+https://... rather than PyPI, which CAO isn't on)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
haofeif added a commit that referenced this pull request Apr 22, 2026
…efs (#197)

- Remove "Persistent agent memory system" bullet — PR #179 is still open
  and was not part of this release
- Add missing #106 (stale spinners blocking Claude Code inbox delivery)
- Add missing #135 to cryptography version-bump coverage (46.0.5 → 46.0.7)
- Correct #137 and #157 references to "resolves" — they are issues, not PRs
- Fix install hint in #169 bullet to use real install path
  (uv tool install git+https://... rather than PyPI, which CAO isn't on)

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants