Repository navigation
feat(build): bundle wrapper to dist/, build-check CI, desktop-app README - #4
Conversation
…top-app README
The Codex plugin marketplace does not document an install hook, so a
fresh 'codex plugin marketplace add' produces a plugin cache with no
node_modules. The wrapper would import @modelcontextprotocol/sdk and
crash at startup. Bundling the wrapper + SDK into a single committed
artifact at plugins/browseros/dist/wrapper.js makes a marketplace add
work end-to-end without extra setup on the user's side.
What changed:
- plugins/browseros/package.json: SDK moved to devDependencies (it is
inlined at build time, not needed at runtime). New 'build' script:
'bun build server/wrapper.js --outfile dist/wrapper.js --target node
--format esm --external node:*'. Initially tried bunup, but bunup's
bundle path mangles Zod's lazy method names and crashes at startup
with 'util3 is not defined'. bun build directly produces a working
bundle.
- plugins/browseros/dist/wrapper.js: committed bundle. 574 KB, 16 ms
to rebuild. node:* imports stay external (Node provides them); SDK
and all transitive deps inline.
- plugins/browseros/.mcp.json: command args updated from
'./server/wrapper.js' to './dist/wrapper.js'. Codex spawns the
bundle.
- plugins/browseros/bun.lock: pinned lockfile for deterministic CI
builds. Replaces the npm package-lock.json from the earlier import
step; the wrapper no longer has runtime deps so npm vs bun is now a
pure preference, and bun is consistent with what the build script
needs.
- .github/workflows/build-check.yml: runs on PRs that touch the
wrapper source or build inputs. Installs deps with
'bun install --frozen-lockfile', rebuilds, and fails the check if
'git diff --exit-code dist/' finds drift. Actions pinned to SHAs.
- .gitignore: keeps generic dist/ ignored, force-tracks
plugins/browseros/dist/ since that's the committed runtime artifact.
- README.md: rewritten to lead with the Codex DESKTOP APP user. The
prior copy framed the install around the CLI. Install is one
terminal command ('codex plugin marketplace add ...') then the rest
is managed inside the desktop app's Plugins panel. Config override
moves to the documented ~/.codex/config.toml path. Local-dev
section explains the bun build loop.
Greptile SummaryThis PR bundles the MCP wrapper into
Confidence Score: 4/5Safe to merge; the runtime bundle and entry-point wiring are correct, and the CI guard is sound in structure — the only fragile spot is the unpinned Bun version. The bundling approach, .gitignore carve-out, and .mcp.json update are all correct. The CI workflow's use of bun-version: latest means the Bun codegen used in CI can silently diverge from what the developer ran locally, turning the git diff --exit-code dist/ check into a source of false failures whenever Bun ships a codegen change — without any code actually being wrong. .github/workflows/build-check.yml — the bun-version: latest setting deserves a pinned version number to keep the dist-drift check deterministic. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Developer edits server/*.js] --> B[bun run build]
B --> C[dist/wrapper.js committed to repo]
C --> D[PR opened]
D --> E{paths filter matches?}
E -- No --> F[build-check skipped]
E -- Yes --> G[CI: bun install --frozen-lockfile]
G --> H[CI: bun run build with bun-version latest]
H --> I{git diff --exit-code dist/}
I -- clean --> J[CI passes]
I -- dirty --> K[CI fails: dist out of date]
C --> L[Codex marketplace add]
L --> M[node dist/wrapper.js spawned]
M --> N[stdio MCP server]
N --> O[discoverBaseUrl]
O --> P[BrowserOS HTTP MCP endpoint]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A[Developer edits server/*.js] --> B[bun run build]
B --> C[dist/wrapper.js committed to repo]
C --> D[PR opened]
D --> E{paths filter matches?}
E -- No --> F[build-check skipped]
E -- Yes --> G[CI: bun install --frozen-lockfile]
G --> H[CI: bun run build with bun-version latest]
H --> I{git diff --exit-code dist/}
I -- clean --> J[CI passes]
I -- dirty --> K[CI fails: dist out of date]
C --> L[Codex marketplace add]
L --> M[node dist/wrapper.js spawned]
M --> N[stdio MCP server]
N --> O[discoverBaseUrl]
O --> P[BrowserOS HTTP MCP endpoint]
Prompt To Fix All With AIFix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
.github/workflows/build-check.yml:33-36
Unpinned Bun version can cause the dist-drift check to produce false failures. The build output from `bun build` is not guaranteed to be byte-for-byte identical across Bun minor/patch releases (codegen optimisations, comment formatting, etc.). When CI installs a newer `latest` than the developer used locally, `git diff --exit-code dist/` will report differences even though no source changed, making the CI invariant unreliable and requiring developers to re-run and re-commit `dist/wrapper.js` just to appease a Bun version bump.
```suggestion
- name: Set up Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: "1.2.15"
```
### Issue 2 of 2
.github/workflows/build-check.yml:9-15
The path filter triggers the check when `server/**`, `dist/**`, `package.json`, `bun.lock`, or the workflow file itself changes. A PR that edits only `server/` files will correctly trigger CI; a PR that edits both `server/` and `README.md` will also trigger (any path match is sufficient). The existing filter is correct; worth confirming this intent is documented for future contributors.
Reviews (1): Last reviewed commit: "feat(build): bundle wrapper into dist/wr..." | Re-trigger Greptile |
Greptile flagged 'bun-version: latest' as a source of false-positive dist-drift failures: bun build output is not guaranteed byte-stable across Bun releases, so CI on a newer Bun than the local toolchain can fail the git diff --exit-code check with no actual source change. Pin to the version we are building against locally.
Summary
Three coupled changes that put the plugin in shape for real Codex desktop-app install testing:
plugins/browseros/dist/wrapper.jsso a freshcodex plugin marketplace addactually works. The Codex plugin marketplace docs do not describe an install hook — the plugin cache it produces has nonode_modules. Without bundling, the wrapper'simport '@modelcontextprotocol/sdk/...'crashes at startup.dist/is out of date withserver/. The committed bundle is the runtime artifact; CI guards drift.Why
bun buildand notbunupYou picked bunup for speed and I started there, but bunup's bundle path mangles Zod's lazy-method-installation pattern and the resulting bundle crashes at startup with
ReferenceError: util3 is not defined. Bun's nativebun build(which bunup wraps) produces a working bundle in 16 ms. The build script invokesbun builddirectly:If bunup grows past this Zod bug we can swap back; for now the simpler tool works.
What's in
plugins/browseros/package.jsonbuildscript usingbun build, drop runtime depsplugins/browseros/dist/wrapper.jsplugins/browseros/.mcp.jsonargs: ["./dist/wrapper.js"]plugins/browseros/bun.lockplugins/browseros/package-lock.json.github/workflows/build-check.ymlbun install --frozen-lockfile,bun run build,git diff --exit-code dist/.gitignoredist/ignored; force-trackplugins/browseros/dist/README.md~/.codex/config.toml; local dev with bunSmoke tested locally
bun run buildproducesdist/wrapper.jsnode_modules/and spawningnode dist/wrapper.js: initialize handshake returnsserverInfo: {"name":"browseros-codex","version":"0.1.0"}cleanlyTest plan
git diff --exit-code dist/returns 0 after a fresh build (CI invariant)