fix(build): Bundle built WebUI assets within Python wheel#169
Conversation
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>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #169 +/- ##
=======================================
Coverage ? 91.34%
=======================================
Files ? 50
Lines ? 4230
Branches ? 0
=======================================
Hits ? 3864
Misses ? 366
Partials ? 0
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@abdullahoff @gutosantos82 when you get a chance can you pls help to review this PR? Many thanks. |
There was a problem hiding this comment.
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 --wheelon main →unzip -lshows zero web assets in the wheeluv build --wheelon pr-169 → wheel containscli_agent_orchestrator/web_ui/index.html+assets/*.css
assets/*.js
- Installed via
uv tool install, rancao-serverfrom/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
artifactsdirective correctly force-includes gitignored build output into the wheel index.htmlexistence 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 👍
|
Please rebase before merging |
|
@abdullahoff feature branch should be up-to-date with main after merge |
* 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)
- 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>
…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>
Problem
Running
cao-serverin production mode returns404athttp://localhost:9889/unless it's launched from inside a cloned copy of the repo viauv run. The REST API works; only the static UI is missing. Users who install CAO viauv 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.pyresolves the static-file directory with a__file__-relative path walk that only lands on a realweb/dist/when the package is editable-installed inside the original repo:The hatch wheel config at
pyproject.tomlonly packagessrc/cli_agent_orchestrator/, soweb/dist/is never copied into the wheel. For any non-editable install, the 4-parent walk lands somewhere insidesite-packages/where noweb/dist/exists, theWEB_DIST.exists()guard returnsFalse, the static mount is silently skipped, andGET /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.resourcesso it works identically for editable and wheel installs. Three coordinated changes:build.outDiratsrc/cli_agent_orchestrator/web_ui/sonpm run buildwrites directly into the package tree.api/main.pyto anchor on the package viaimportlib.resources.files("cli_agent_orchestrator") / "web_ui", guarded onindex.htmlexistence (graceful no-op if the bundle wasn't built).[tool.hatch.build].artifacts. Noforce-includeneeded — the directory already lives under the packaged tree, soartifactsalone is sufficient to un-exclude the gitignored files.After these changes,
cao-serverlaunched from any cwd — including outside the repo — resolves the UI from insidesite-packages/and serves it.uv tool installusers get the Web UI out of the box with no clone required.Key Changes
web/vite.config.ts— addbuild.outDir: '../src/cli_agent_orchestrator/web_ui'withemptyOutDir: true.src/cli_agent_orchestrator/api/main.py— replace the__file__-relative walk withPath(str(_pkg_files("cli_agent_orchestrator") / "web_ui")); checkindex.htmlinstead of the directory to avoid false-positives on an emptyweb_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— addsrc/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 --wheelproduces a clean wheel (no duplicate-name warnings) containingcli_agent_orchestrator/web_ui/index.html+ the fullassets/subtree — confirmed viaunzip -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) returnsHTTP 200at/and serves the bundledindex.html— the original 404 is gone./healthcontinues to return 200, confirming API routes are unaffected.Follow-up
Maintainers cutting releases should run
cd web && npm run buildbeforeuv build --wheelto ensure the wheel contains a fresh bundle; a hatch build hook could automate this in a future PR.