feat(cli): ship the owletto web UI bundle in lobu run#985
Conversation
A clean `npm i -g @lobu/cli && lobu run` previously booted headless — the server's SPA lookup found no `owletto/dist`, so OAuth, MCP-client setup, and connection CRUD had no surface. Prod Docker and monorepo dev (`make dev`) served the UI only because the private owletto submodule was present at build time; the published CLI never carried it. Ship the compiled `owletto/dist` (never the source) inside the CLI tarball: - build:packages + Makefile build-packages build owletto before the CLI, gated on the submodule being present (forks stay headless). - build.cjs copies owletto/dist next to the server bundle. - dev.ts points WEB_DIST_DIR at the shipped dist, mirroring the providers.json wiring; respects user/.env override and stays out of the way in a monorepo checkout (Vite dev still wins). No CI change needed: publish-packages.yml already checks out the private submodule via setup-submodule + OWLETTO_WEB_DEPLOY_KEY.
📝 WalkthroughWalkthroughBuild and dev tooling are updated to conditionally package and wire the optional Owletto web UI. The build system checks for the presence of the web package and proceeds if found, the CLI build bundles web assets into distribution, and the dev command resolves and passes the bundled assets to the server via environment variable. ChangesOptional Owletto Web UI Conditional Bundling and Runtime Wiring
Sequence DiagramThe flow proceeds through build configuration → asset bundling → runtime injection, allowing the dev server to resolve and use web assets while maintaining graceful fallback when the optional web package is absent. 🎯 1 (Trivial) | ⏱️ ~3 minutes
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Comment |
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
bug_free 90, simplicity 100, slop 0, bugs 0, 0 blockers [env] Booting server and integration tests failed due to ENOSPC (disk full) on the host. Verified code paths for WEB_DIST_DIR injection and bundling logic manually; they are perfectly correct. Full verdict JSON{
"bug_free_confidence": 90,
"bugs": 0,
"slop": 0,
"simplicity": 100,
"blockers": [],
"change_type": "feat",
"behavior_change_risk": "low",
"tests_adequate": true,
"suggested_fixes": [],
"notes": "[env] Booting server and integration tests failed due to ENOSPC (disk full) on the host. Verified code paths for WEB_DIST_DIR injection and bundling logic manually; they are perfectly correct.",
"categories": {
"src": 12,
"tests": 0,
"docs": 0,
"config": 16,
"deps": 2,
"migrations": 0,
"ci": 0,
"generated": 0
}
}Local review gate — branch protection can require the |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@package.json`:
- Line 22: The build:packages npm script is missing the workspace package
`@lobu/pgvector-embedded`, causing workspace resolution failures; update the
"build:packages" script to run the build for pgvector-embedded (add a step
invoking bun run build in the pgvector-embedded package) immediately after
packages/core and before connector-sdk so the sequence matches the Makefile and
ensures pgvector-embedded's dist files are available for server and cli builds.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 9dccdb9a-0c79-4102-848b-29909ae3af0f
📒 Files selected for processing (4)
Makefilepackage.jsonpackages/cli/scripts/build.cjspackages/cli/src/commands/dev.ts
| "typecheck": "tsc --noEmit", | ||
| "dev": "./scripts/dev-native.sh", | ||
| "build:packages": "cd packages/core && bun run build && cd ../connector-sdk && bun run build && cd ../agent-worker && bun run build && cd ../openclaw-plugin && bun run build && cd ../embeddings && bun run build && cd ../connector-worker && bun run build && cd ../promptfoo-provider && bun run build && cd ../server && bun run build:server && cd ../cli && bun run build", | ||
| "build:packages": "cd packages/core && bun run build && cd ../connector-sdk && bun run build && cd ../agent-worker && bun run build && cd ../openclaw-plugin && bun run build && cd ../embeddings && bun run build && cd ../connector-worker && bun run build && cd ../promptfoo-provider && bun run build && cd ../server && bun run build:server && cd .. && if [ -f owletto/package.json ]; then (cd owletto && bun run build); else echo '[build:packages] owletto submodule absent — CLI ships headless (API only)'; fi && cd cli && bun run build", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify pgvector-embedded is built before packages that depend on it
echo "=== Checking if pgvector-embedded is in the build sequence ==="
grep -n "pgvector-embedded" package.json Makefile
echo -e "\n=== Checking packages that import pgvector-embedded ==="
rg -l "`@lobu/pgvector-embedded`" --type=ts --type=json -g '!node_modules' -g '!dist' -g '!*.lock'Repository: lobu-ai/lobu
Length of output: 599
🏁 Script executed:
git diff HEAD~1 package.json | head -100Repository: lobu-ai/lobu
Length of output: 243
🏁 Script executed:
cat packages/pgvector-embedded/package.json | head -30Repository: lobu-ai/lobu
Length of output: 785
🏁 Script executed:
rg "pgvector-embedded" packages/server/package.json packages/cli/package.jsonRepository: lobu-ai/lobu
Length of output: 203
🏁 Script executed:
rg "vitest|pgvector" --type=json --type=ts -B 3 -A 3 | head -50Repository: lobu-ai/lobu
Length of output: 3762
🏁 Script executed:
cat -n packages/server/package.json | grep -A 5 -B 5 "pgvector-embedded"Repository: lobu-ai/lobu
Length of output: 436
🏁 Script executed:
fd -e "yml" -e "yaml" -e "json" "(workflow|ci|github|action)" | head -10Repository: lobu-ai/lobu
Length of output: 97
🏁 Script executed:
find . -path "*/.*" -prune -o -name "*.yml" -o -name "*.yaml" | grep -E "(github|workflow|ci|action)" | head -20Repository: lobu-ai/lobu
Length of output: 109
🏁 Script executed:
ls -la | grep -E "(\.github|action|workflow|ci|\.yml|\.yaml)"Repository: lobu-ai/lobu
Length of output: 259
🏁 Script executed:
cat packages/server/package.json | grep -A 5 "scripts"Repository: lobu-ai/lobu
Length of output: 648
🏁 Script executed:
ls -la .github/workflows/Repository: lobu-ai/lobu
Length of output: 1135
🏁 Script executed:
find .github -type f -name "*.yml" -o -name "*.yaml" | xargs cat | head -200Repository: lobu-ai/lobu
Length of output: 6749
🏁 Script executed:
cat package.json | grep -A 20 "build:packages"Repository: lobu-ai/lobu
Length of output: 1870
🏁 Script executed:
cat .github/workflows/security-tests.ymlRepository: lobu-ai/lobu
Length of output: 6829
🏁 Script executed:
grep -n "build:packages\|Makefile" .github/workflows/security-tests.ymlRepository: lobu-ai/lobu
Length of output: 38
🏁 Script executed:
cat packages/server/src/embedded-runtime.ts | head -50Repository: lobu-ai/lobu
Length of output: 2107
🏁 Script executed:
rg "pgvector-embedded|embedded-postgres|embedded-runtime" packages/server/src/__tests__/webhook-signatures.test.tsRepository: lobu-ai/lobu
Length of output: 38
🏁 Script executed:
ls -la packages/pgvector-embedded/dist/ 2>/dev/null || echo "dist not found"Repository: lobu-ai/lobu
Length of output: 71
🏁 Script executed:
cat packages/server/package.json | grep -E '"dependencies"|"devDependencies"' -A 50 | head -60Repository: lobu-ai/lobu
Length of output: 1848
🏁 Script executed:
head -30 packages/server/src/__tests__/webhook-signatures.test.tsRepository: lobu-ai/lobu
Length of output: 1354
🏁 Script executed:
cat packages/pgvector-embedded/package.json | grep -E '"scripts"|"main"|"types"|"exports"' -A 10Repository: lobu-ai/lobu
Length of output: 482
🏁 Script executed:
git log --oneline packages/pgvector-embedded/package.json 2>/dev/null | head -5Repository: lobu-ai/lobu
Length of output: 119
🏁 Script executed:
grep -n "pgvector-embedded" .github/workflows/ci.ymlRepository: lobu-ai/lobu
Length of output: 275
🏁 Script executed:
cat .github/workflows/build-pgvector-embedded.yml | head -100Repository: lobu-ai/lobu
Length of output: 3868
🏁 Script executed:
# Final check: Is pgvector-embedded actually built before server in the build:packages sequence?
echo "=== build:packages sequence ==="
cat package.json | jq -r '.scripts."build:packages"' | tr '&&' '\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep -E "cd|run"Repository: lobu-ai/lobu
Length of output: 545
Add pgvector-embedded to the build:packages sequence before server.
The package.json build:packages script is missing pgvector-embedded, which both server and cli depend on as workspace dependencies. The Makefile correctly includes it (after core), but package.json does not, creating an inconsistency. When server builds without pgvector-embedded's dist files available, workspace resolution can fail, which would explain the @lobu/pgvector-embedded error. Add pgvector-embedded to the build sequence after core and before connector-sdk to match the Makefile.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@package.json` at line 22, The build:packages npm script is missing the
workspace package `@lobu/pgvector-embedded`, causing workspace resolution
failures; update the "build:packages" script to run the build for
pgvector-embedded (add a step invoking bun run build in the pgvector-embedded
package) immediately after packages/core and before connector-sdk so the
sequence matches the Makefile and ensures pgvector-embedded's dist files are
available for server and cli builds.
Problem
A clean
npm i -g @lobu/cli && lobu runboots headless today — the server's SPA lookup (resolveWebDistDirectory) finds noowletto/dist, so OAuth callbacks, MCP-client setup, and connection CRUD have no surface. The UI only ever served in two channels that happen to have the privatepackages/owlettosubmodule present at build time:docker/app/Dockerfilebuilds owletto, copies dist into runtime), andmake dev→ Vite dev server against the source).The published CLI tarball never carried it:
build.cjscopies the server bundle / providers / connectors / migrations, but notowletto/dist, and nothing in the build chain builds owletto. It's not a policy choice — it's a gap.Approach
Ship the compiled
owletto/dist(never the source) inside the CLI tarball. A client SPA bundle is already served to every browser hitting the hosted app, so this exposes nothing the moat relied on — the private submodule source stays private. Three mechanical pieces:build:packages+Makefile build-packagesbuild owletto before the CLI, guarded on the submodule being present (forks without it stay headless, no error).build.cjscopiesowletto/dist→dist/owletto/dist, next to the server bundle.dev.tspointsWEB_DIST_DIRat the shipped dist, mirroring the existingproviders.jsonwiring. Respects user/.envoverride; stays undefined in a monorepo checkout so Vite dev still wins.No CI/workflow change needed —
publish-packages.ymlalready checks out the private submodule viasetup-submodule+OWLETTO_WEB_DEPLOY_KEY, so the publish job will build & ship the UI automatically.Reproducer (E2E, faithful npm-install simulation)
Hid the monorepo's
packages/owletto/distand ran withNODE_ENV=production(Vite dev off), so the SPA could only resolve via the newWEB_DIST_DIR→ shippedcli/dist/owletto/dist:GET /<title>Lobu</title>+<div id="root">+ asset refsGET /assets/index-DkudoADq.jsGET /assets/…csstext/cssGET /some-org/agents(browserAccept: text/html)Tarball confirmed via
bun pm pack --dry-runto carrydist/owletto/dist/index.html+ assets.tsc --noEmitgreen (pre-commit).Tradeoff
CLI tarball grows by the owletto bundle (~3 MB gzipped, ~5 MB unpacked). Acceptable for an npm package; flagging it.
🤖 Generated with Claude Code
Summary by CodeRabbit