ui-tui: bundle with esbuild, drop runtime node_modules#18036
Merged
Conversation
OutThisLife
approved these changes
Apr 30, 2026
Contributor
There was a problem hiding this comment.
Pull request overview
Refactors the Hermes TUI build/distribution pipeline to produce a single bundled dist/entry.js with esbuild, allowing Nix (and other packaged layouts) to ship the TUI without runtime node_modules.
Changes:
- Add an esbuild-based bundler script to generate a self-contained
dist/entry.js. - Update Nix packaging/checks to stop copying/asserting runtime
node_modules. - Adjust the CLI’s TUI launcher logic to skip npm installs for the prebuilt-bundle layout (no
package-lock.json+dist/entry.jspresent).
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| ui-tui/scripts/build.mjs | New esbuild bundling script producing a single dist/entry.js and stripping the shebang. |
| ui-tui/package.json | Replace build pipeline with esbuild script; simplify dev script. |
| nix/tui.nix | Stop installing/copying runtime node_modules; ship only dist/ + package.json. |
| nix/checks.nix | Remove assertion that node_modules exists in the packaged TUI. |
| hermes_cli/main.py | Skip npm install when running from a prebuilt, self-contained TUI bundle layout. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
8f418ff to
040867a
Compare
Replace the tsc + babel pipeline with a single esbuild invocation that produces a self-contained dist/entry.js. The nix TUI derivation no longer copies node_modules — only dist/ + package.json ship, shrinking the output from hundreds of MB to ~2.9 MB. - ui-tui/scripts/build.mjs: new esbuild bundler. Aliases @hermes/ink to source (esbuild's __esm helper doesn't await nested async init, which breaks lazy-assigned exports like 'render' when re-exporting through a prebuilt submodule). Stubs react-devtools-core (dev-only). Injects a createRequire shim for transitive CJS deps. Strips the shebang from src/entry.tsx because Nix patchShebangs mangles '/usr/bin/env -S node --max-old-space-size=8192 --expose-gc' — it drops the 'node' token. The Python launcher always invokes node explicitly, so the shebang is redundant. - nix/tui.nix: installPhase no longer copies node_modules or the @hermes/ink packages dir. - nix/checks.nix: drop the 'node_modules present' assertion. - hermes_cli/main.py: _tui_need_npm_install short-circuits when dist/entry.js exists and no package-lock.json is present. That is the prebuilt-bundle layout (nix / packaged release) and there is nothing to install. Without this, the launcher tried to npm install in a non-existent site-packages/ui-tui path.
040867a to
42627b4
Compare
the esbuild pipeline (scripts/build.mjs) already bundles ink into a single self-contained dist/entry.js. remove the Dockerfile steps that manually copied packages/hermes-ink into node_modules/@hermes/ink and ran a nested npm install there. - Dockerfile: simplify TUI build step to just 'npm run build' - hermes_cli/main.py: _tui_build_needed now checks dist/entry.js staleness against source files before falling back to the old ink-bundle.js logic - tests: update TUI npm install tests and drop the Dockerfile contract test for the removed ink materialization step
Contributor
🔎 Lint report:
|
The old mtime-tracking staleness machinery (_tui_build_needed, _hermes_ink_bundle_stale, _find_bundled_tui) tried to avoid rebuilding by comparing source timestamps to dist/entry.js. This was fragile and added ~100 lines of code. Replace with three clear paths: 1. HERMES_TUI_DIR set (prebuilt/nix): just node dist/entry.js, no build 2. --dev mode: tsx src/entry.tsx, no build, hot reload 3. Normal: always npm run build (esbuild is ~1s, correctness > caching) Also error when HERMES_TUI_DIR is set with --dev (footgun: prebuilt bundle has no source code to hot-reload).
This was referenced May 12, 2026
02356abc
pushed a commit
to 02356abc/hermes-agent
that referenced
this pull request
May 14, 2026
ui-tui: bundle with esbuild, drop runtime node_modules
jsboige
pushed a commit
to jsboige/hermes-agent
that referenced
this pull request
May 14, 2026
ui-tui: bundle with esbuild, drop runtime node_modules
19 tasks
gweeteve
pushed a commit
to gweeteve/hermes-agent
that referenced
this pull request
Jun 2, 2026
ui-tui: bundle with esbuild, drop runtime node_modules
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Replace the tsc + babel pipeline with a single esbuild invocation that produces a self-contained dist/entry.js. The nix TUI derivation no longer copies node_modules — only dist/ + package.json ship, shrinking the output from hundreds of MB to ~2.9 MB.
Also simplifies the TUI launcher: rips out the staleness-tracking maze (~100 LOC of mtime walks across source trees) in favor of three clear paths — prebuilt, dev, always-build. esbuild is fast enough (~1s) that caching the build was more bug surface than
win.
Type of Change
Changes Made
react-devtools-core (dev-only). Injects a createRequire shim for transitive CJS deps. Strips the shebang from src/entry.tsx because Nix patchShebangs mangles '/usr/bin/env -S node --max-old-space-size=8192 --expose-gc' — it drops the 'node' token. The Python
launcher always invokes node explicitly, so the shebang is redundant.
_tui_need_npm_installshort-circuits when dist/entry.js exists and no package-lock.json is present. That is the prebuilt-bundle layout (nix / packaged release) and there is nothing to install. Without this, the launcher tried to npm install in anon-existent site-packages/ui-tui path.
_make_tui_argvinto three explicit paths: (1)HERMES_TUI_DIRset → run prebuiltdist/entry.js; (2)--dev→tsx src/entry.tsx(no build); (3) normal → alwaysnpm run build, then run._find_bundled_tui,_tui_build_needed,_hermes_ink_bundle_stale.--dev+HERMES_TUI_DIRerrors out instead of silently failing (prebuilt bundle has no source to hot-reload)._tui_need_npm_installtests.How to Test
hermes --tui --devwithHERMES_TUI_DIRset → should error out cleanly:3
Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/A