Skip to content

fix(browse): externalize @ngrok/ngrok so Node server bundle builds on Windows#1019

Closed
tomasmontbrun-hash wants to merge 1 commit into
garrytan:mainfrom
tomasmontbrun-hash:fix/windows-node-server-bundle-ngrok-external
Closed

fix(browse): externalize @ngrok/ngrok so Node server bundle builds on Windows#1019
tomasmontbrun-hash wants to merge 1 commit into
garrytan:mainfrom
tomasmontbrun-hash:fix/windows-node-server-bundle-ngrok-external

Conversation

@tomasmontbrun-hash

Copy link
Copy Markdown

Summary

browse/scripts/build-node-server.sh fails on a fresh install on Windows with:

Building Node-compatible server bundle...
error: cannot write multiple output files without an output directory

This breaks the browser-related skills on Windows after ./setup: /browse, /canary, /pair-agent, /open-gstack-browser, /setup-browser-cookies, and /design-review (all of which rely on server-node.mjs).

Root cause

browse/src/server.ts has two await import('@ngrok/ngrok') calls (around lines 1588 and 2368). Bun treats these dynamic imports as a code-splitting signal and wants to emit a separate chunk for @ngrok/ngrok. Combined with --outfile (single-file output, used by the script), bun refuses with the "cannot write multiple output files without an output directory" error.

The other skipped externals in the same bun build invocation (playwright, playwright-core, diff, bun:sqlite) use exactly the pattern needed here.

Fix

Add --external "@ngrok/ngrok" to the bun build invocation. The dynamic import then resolves at runtime via node_modules/@ngrok/ngrok instead of being bundled, no chunks emitted, single-file output succeeds.

@ngrok/ngrok is already declared in package.json ("^1.7.0"), so it's reachable at runtime under Node.

Verification

$ bash browse/scripts/build-node-server.sh
Building Node-compatible server bundle...

  Bundled 25 modules in 52ms

  server-node.mjs  0.33 MB  (entry point)

Node server bundle ready: /c/Users/tomas/.claude/skills/gstack/browse/dist/server-node.mjs

Confirmed browse.exe then launches the bundled server ([browse] Starting server... → command list) on:

  • Windows 11 Home 26200
  • Git Bash 2.53.0.windows.2
  • bun 1.3.11
  • Node v25.2.1

Test plan

  • Run ./setup --host claude on a fresh Windows install — expect no build error.
  • Run a /browse goto … command after install — expect server to start and respond.

build-node-server.sh fails on Windows with:
  error: cannot write multiple output files without an output directory

Root cause: server.ts has two `await import('@ngrok/ngrok')` calls
(lines 1588 and 2368), which makes bun emit a separate chunk for the
ngrok module. Combined with `--outfile` (single-file output), bun
refuses to write the multiple files.

Fix: pass `--external "@ngrok/ngrok"` to the bun build invocation,
matching the pattern already used for playwright, playwright-core,
diff, and bun:sqlite. The dynamic import then resolves at runtime
via node_modules instead of being bundled.

Verified on Windows 11 / Git Bash / bun 1.3.11:
  Bundled 25 modules in 52ms
  server-node.mjs  0.33 MB  (entry point)

Restores /gstack-browse, /gstack-canary, /gstack-pair-agent,
/gstack-open-gstack-browser, /gstack-setup-browser-cookies,
and /gstack-design-review on Windows.
@garrytan

Copy link
Copy Markdown
Owner

Closing — duplicate of several PRs fixing the same @ngrok/ngrok Windows build issue. We'll pick the best approach and merge separately. Thank you for the contribution!

@garrytan garrytan closed this Apr 16, 2026
garrytan added a commit that referenced this pull request Apr 16, 2026
… Windows

@ngrok/ngrok has a native .node addon that causes `bun build --outfile` to
fail with "cannot write multiple output files without an output directory".
Externalize it alongside the existing runtime deps (playwright, diff,
bun:sqlite), matching the exact pattern used for every other dynamic import
in server.ts.

Adds a policy comment explaining when to extend the externals list so the
next native dep doesn't repeat this failure.

Two community contributors independently converged on this fix:
 - @tomasmontbrun-hash (#1019)
 - @scarson (#1013)
Also fixes issues #1010 and #960.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
garrytan added a commit that referenced this pull request Apr 16, 2026
Fixes shipped in this version:
- Externalize @ngrok/ngrok so the Node server bundle builds on Windows
  (PRs #1019, #1013; issues #1010, #960)
- Shell precedence fix so build/test failures no longer exit 0 in CI
- Build validation test for server-node.mjs
- Windows setup verifies @ngrok/ngrok native binary is loadable

Credit: @tomasmontbrun-hash (#1019), @scarson (#1013).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@garrytan

Copy link
Copy Markdown
Owner

Thanks @tomasmontbrun-hash — landed your fix in #1024 (v0.18.0.1) along with the CI error-swallowing fix that let this slip through in the first place. Your diagnosis and one-line fix were spot on.

Closing in favor of #1024, which credits you in the CHANGELOG and commit body. Thanks again for tracking this down on a fresh Windows install — this was breaking /browse, /canary, /pair-agent, /open-gstack-browser, /setup-browser-cookies, and /design-review for every Windows user since v0.15.12.

garrytan added a commit that referenced this pull request Apr 16, 2026
…1024)

* fix(browse): externalize @ngrok/ngrok so Node server bundle builds on Windows

@ngrok/ngrok has a native .node addon that causes `bun build --outfile` to
fail with "cannot write multiple output files without an output directory".
Externalize it alongside the existing runtime deps (playwright, diff,
bun:sqlite), matching the exact pattern used for every other dynamic import
in server.ts.

Adds a policy comment explaining when to extend the externals list so the
next native dep doesn't repeat this failure.

Two community contributors independently converged on this fix:
 - @tomasmontbrun-hash (#1019)
 - @scarson (#1013)
Also fixes issues #1010 and #960.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(package.json): subshell cleanup so || true stops masking build/test failures

Shell operator precedence trap in both the build and test scripts:

    cmd1 && cmd2 && ... && rm -f .*.bun-build || true
    bun test ... && bun run slop:diff 2>/dev/null || true

The trailing `|| true` was intended to suppress cleanup errors, but it
applies to the entire `&&` chain — so ANY failure (including the
build-node-server.sh failure that broke Windows installs since v0.15.12)
silently exits 0. CI ran the build, the build failed, and CI reported green.

Wrap the cleanup/slop-diff commands in subshells so `|| true` only scopes to
the intended step:

    ... && (rm -f .*.bun-build || true)
    bun test ... && (bun run slop:diff 2>/dev/null || true)

Verified: `bash -c 'false && echo A && rm -f X || true'` exits 0 (old,
broken), `bash -c 'false && echo A && (rm -f X || true)'` exits 1 (new,
correct).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(browse): add build validation test for server-node.mjs

Two assertions:
1. `node --check` passes on the built `server-node.mjs` (valid ES module
   syntax). This catches regressions where the post-processing steps (perl
   regex replacements) corrupt the bundle.
2. No inlined `@ngrok/ngrok` module identifiers (ngrok_napi, platform-
   specific binding packages). Verifies the --external flag actually kept
   it external.

Skips gracefully when `browse/dist/server-node.mjs` is missing — the dist
dir is gitignored, so a fresh clone + `bun test` without a prior build is
a valid state, not a failure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(setup): verify @ngrok/ngrok can load on Windows

Mirror the existing Playwright verification step. Since @ngrok/ngrok is
now externalized in server-node.mjs (resolved at runtime from node_modules),
confirm the platform-specific native binary (@ngrok/ngrok-win32-x64-msvc et
al.) is installed at setup time rather than surfacing the failure later
when the user runs /pair-agent.

Same fallback pattern: if `node -e "require('@ngrok/ngrok')"` fails, fall
back to `npm install --no-save @ngrok/ngrok` to pull the missing binary.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: bump to v0.18.0.1 for ngrok Windows fix + CI error-propagation

Fixes shipped in this version:
- Externalize @ngrok/ngrok so the Node server bundle builds on Windows
  (PRs #1019, #1013; issues #1010, #960)
- Shell precedence fix so build/test failures no longer exit 0 in CI
- Build validation test for server-node.mjs
- Windows setup verifies @ngrok/ngrok native binary is loadable

Credit: @tomasmontbrun-hash (#1019), @scarson (#1013).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mathiasmora2232 pushed a commit to mathiasmora2232/gstack that referenced this pull request May 30, 2026
…arrytan#1024)

* fix(browse): externalize @ngrok/ngrok so Node server bundle builds on Windows

@ngrok/ngrok has a native .node addon that causes `bun build --outfile` to
fail with "cannot write multiple output files without an output directory".
Externalize it alongside the existing runtime deps (playwright, diff,
bun:sqlite), matching the exact pattern used for every other dynamic import
in server.ts.

Adds a policy comment explaining when to extend the externals list so the
next native dep doesn't repeat this failure.

Two community contributors independently converged on this fix:
 - @tomasmontbrun-hash (garrytan#1019)
 - @scarson (garrytan#1013)
Also fixes issues garrytan#1010 and garrytan#960.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(package.json): subshell cleanup so || true stops masking build/test failures

Shell operator precedence trap in both the build and test scripts:

    cmd1 && cmd2 && ... && rm -f .*.bun-build || true
    bun test ... && bun run slop:diff 2>/dev/null || true

The trailing `|| true` was intended to suppress cleanup errors, but it
applies to the entire `&&` chain — so ANY failure (including the
build-node-server.sh failure that broke Windows installs since v0.15.12)
silently exits 0. CI ran the build, the build failed, and CI reported green.

Wrap the cleanup/slop-diff commands in subshells so `|| true` only scopes to
the intended step:

    ... && (rm -f .*.bun-build || true)
    bun test ... && (bun run slop:diff 2>/dev/null || true)

Verified: `bash -c 'false && echo A && rm -f X || true'` exits 0 (old,
broken), `bash -c 'false && echo A && (rm -f X || true)'` exits 1 (new,
correct).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(browse): add build validation test for server-node.mjs

Two assertions:
1. `node --check` passes on the built `server-node.mjs` (valid ES module
   syntax). This catches regressions where the post-processing steps (perl
   regex replacements) corrupt the bundle.
2. No inlined `@ngrok/ngrok` module identifiers (ngrok_napi, platform-
   specific binding packages). Verifies the --external flag actually kept
   it external.

Skips gracefully when `browse/dist/server-node.mjs` is missing — the dist
dir is gitignored, so a fresh clone + `bun test` without a prior build is
a valid state, not a failure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(setup): verify @ngrok/ngrok can load on Windows

Mirror the existing Playwright verification step. Since @ngrok/ngrok is
now externalized in server-node.mjs (resolved at runtime from node_modules),
confirm the platform-specific native binary (@ngrok/ngrok-win32-x64-msvc et
al.) is installed at setup time rather than surfacing the failure later
when the user runs /pair-agent.

Same fallback pattern: if `node -e "require('@ngrok/ngrok')"` fails, fall
back to `npm install --no-save @ngrok/ngrok` to pull the missing binary.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: bump to v0.18.0.1 for ngrok Windows fix + CI error-propagation

Fixes shipped in this version:
- Externalize @ngrok/ngrok so the Node server bundle builds on Windows
  (PRs garrytan#1019, garrytan#1013; issues garrytan#1010, garrytan#960)
- Shell precedence fix so build/test failures no longer exit 0 in CI
- Build validation test for server-node.mjs
- Windows setup verifies @ngrok/ngrok native binary is loadable

Credit: @tomasmontbrun-hash (garrytan#1019), @scarson (garrytan#1013).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants