Skip to content

fix(electron): include @ngrok/ngrok in packaged app so tunnel feature works#2037

Open
bsradcliffe wants to merge 10 commits intoMCPJam:mainfrom
bsradcliffe:claude/funny-kirch-327291
Open

fix(electron): include @ngrok/ngrok in packaged app so tunnel feature works#2037
bsradcliffe wants to merge 10 commits intoMCPJam:mainfrom
bsradcliffe:claude/funny-kirch-327291

Conversation

@bsradcliffe
Copy link
Copy Markdown

@bsradcliffe bsradcliffe commented May 8, 2026

Fixes #2036.

Summary

Fixes Cannot find module '@ngrok/ngrok' crash on startup of the packaged Mac app.

The VitePlugin only packs .vite/build/ into app.asar — no node_modules. Since @ngrok/ngrok is listed as external in vite.main.config.ts (native addons can't be bundled by Vite), it was completely absent from the packaged app. The existing afterCopy hook was writing to buildPath/node_modules/@ngrok, which is outside what VitePlugin packs — effectively a no-op.

Changes (mcpjam-inspector/forge.config.ts)

  1. afterCopy: copy the @ngrok scope dir into .vite/build/node_modules/ so it lands inside the asar alongside main.cjs. Uses require.resolve() to find the package accounting for npm workspace hoisting. Copies only the platform-matched native sub-package (e.g. ngrok-darwin-arm64) to avoid bundling redundant binaries for other platforms.
  2. asar.unpack glob: "**/*.node""*.node". @electron/asar calls minimatch(absPath, pattern, { matchBase: true }) — patterns containing / don't match absolute paths, so only basename-only patterns work. afterCopy runs before asarApp() creates the archive, so the .node files placed by the hook are correctly seen and marked as unpacked at creation time.

Test plan

  • Build with electron-forge package and confirm app.asar.unpacked/.vite/build/node_modules/@ngrok/ngrok-darwin-arm64/ngrok.darwin-arm64.node exists
  • Launch the packaged .app and confirm no Cannot find module '@ngrok/ngrok' error on startup
  • Test the ngrok tunnel feature end-to-end

… works

The VitePlugin only packs .vite/build/ into app.asar — no top-level node_modules.
@ngrok/ngrok was marked external in vite.main.config.ts (can't bundle native addons),
so it was never reachable at runtime and caused 'Cannot find module' on startup.

Three changes to forge.config.ts:
1. afterCopy: copy the @ngrok scope dir into .vite/build/node_modules/ so it lands
   inside the asar alongside main.cjs. Uses require.resolve() to handle npm workspace
   hoisting instead of hardcoding the local node_modules path.
2. postPackage hook: rebuild the asar with unpack: "*.node" after packaging.
   The asar.unpack packagerConfig option only evaluates files present before afterCopy
   runs, so .node binaries added by afterCopy were never promoted to app.asar.unpacked.
   Native addons cannot be dlopen'd from inside an asar archive, so this step is required.
3. Fix asar.unpack pattern from "**/*.node" to "*.node" — @electron/asar uses
   minimatch with matchBase:true against absolute paths, so patterns with "/" don't match.
@chelojimenez
Copy link
Copy Markdown
Contributor

chelojimenez commented May 8, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@bsradcliffe bsradcliffe marked this pull request as ready for review May 8, 2026 00:46
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@dosubot dosubot Bot added the size:M This PR changes 30-99 lines, ignoring generated files. label May 8, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 8, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Hidden review stack artifact:

Walkthrough

The changes refactor forge.config.ts to improve native module handling for the @ngrok/ngrok package in the macOS application build. The asar unpacking pattern is narrowed to *.node. The afterCopy hook now resolves @ngrok/ngrok via require.resolve and copies only the JS wrapper plus the platform/arch native package into .vite/build/node_modules/@ngrok. A new postPackage hook extracts and rebuilds app.asar with unpack: "*.node" so native addons are placed in app.asar.unpacked.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
mcpjam-inspector/forge.config.ts (1)

202-204: ⚡ Quick win

Document or guard the ASAR rebuild to prevent future breakage with asarIntegrity.

The postPackage hook rebuilds app.asar, which is safe today since no asarIntegrity option is present in packagerConfig. However, EnableEmbeddedAsarIntegrityValidation is already enabled (line 243), and adding asarIntegrity to the packager config would cause the rebuilt ASAR to silently fail integrity validation when loaded—the header hash embedded by electron-packager would no longer match.

Preempt this by adding a comment explaining the constraint alongside the fuse configuration, or implement a guard in postPackage that updates the Info.plist header hash if one exists.

🤖 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 `@mcpjam-inspector/forge.config.ts` around lines 202 - 204, The postPackage
hook currently rebuilds app.asar via asar.createPackageWithOptions in
postPackage which will break electron-packager's asarIntegrity validation if
packagerConfig.asarIntegrity is later set; either add a clear comment next to
the fuse/EnableEmbeddedAsarIntegrityValidation configuration documenting that
rebuilding app.asar is incompatible with asarIntegrity, or implement a guard in
postPackage: detect if packagerConfig.asarIntegrity (or an embedded asar header
hash in Info.plist) exists and skip the rebuild or recompute and update the
stored header hash in Info.plist after calling asar.createPackageWithOptions;
reference postPackage, asar.createPackageWithOptions,
EnableEmbeddedAsarIntegrityValidation, packagerConfig.asarIntegrity and the
Info.plist header hash when making the change.
🤖 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 `@mcpjam-inspector/forge.config.ts`:
- Around line 111-114: The code currently silently skips copying when src
doesn't exist, leading to a cryptic runtime MODULE_NOT_FOUND; update the block
that checks existsSync(src) around the cpSync(src, join(dest, pkg), { recursive:
true }) call to emit a clear build-time warning (e.g. console.warn) when the
platform package (pkg / src) is missing, including the offending pkg and dest
values so users can see that the `@ngrok/`<pkg> binary was not found instead of
silently proceeding.
- Around line 192-207: postPackage currently renames the original asar
(renameSync(asarPath, `${asarPath}.bak`)) before repackaging and never removes
the extracted tmpDir, so failures leave the app broken and temp files behind;
wrap the sequence around asar.extractAll(tmpDir), renameSync, and
asar.createPackageWithOptions in a try/finally: create tmpDir, extract (remove
the needless await on asar.extractAll if synchronous), then rename the original
to .bak only immediately before createPackageWithOptions, and in finally ensure
tmpDir is removed (use rmSync or similar) and if createPackageWithOptions failed
restore the original by renaming `${asarPath}.bak` back to asarPath; only
unlinkSync(`${asarPath}.bak`) after successful packaging. Ensure you reference
tmpDir, asarPath, asar.extractAll, renameSync, asar.createPackageWithOptions,
unlinkSync and rmSync when making the change.
- Around line 109-115: The loop that copies packages using cpSync (iterating
over pkgsToCopy with src built from ngrokSrc) can throw and currently those
errors are unhandled; wrap the cpSync call (and the surrounding per-package
logic) in a try/catch block and on error call the provided callback with the
error (callback(err)) after logging context (e.g., which pkg/src/dest failed),
so permission/full-disk/symlink errors are surfaced instead of crashing or
hanging.

---

Nitpick comments:
In `@mcpjam-inspector/forge.config.ts`:
- Around line 202-204: The postPackage hook currently rebuilds app.asar via
asar.createPackageWithOptions in postPackage which will break
electron-packager's asarIntegrity validation if packagerConfig.asarIntegrity is
later set; either add a clear comment next to the
fuse/EnableEmbeddedAsarIntegrityValidation configuration documenting that
rebuilding app.asar is incompatible with asarIntegrity, or implement a guard in
postPackage: detect if packagerConfig.asarIntegrity (or an embedded asar header
hash in Info.plist) exists and skip the rebuild or recompute and update the
stored header hash in Info.plist after calling asar.createPackageWithOptions;
reference postPackage, asar.createPackageWithOptions,
EnableEmbeddedAsarIntegrityValidation, packagerConfig.asarIntegrity and the
Info.plist header hash when making the change.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b22290fc-0d05-446f-956a-93aa3e3a8232

📥 Commits

Reviewing files that changed from the base of the PR and between 56ebf6f and 9530f6f.

📒 Files selected for processing (1)
  • mcpjam-inspector/forge.config.ts

Comment thread mcpjam-inspector/forge.config.ts Outdated
Comment thread mcpjam-inspector/forge.config.ts Outdated
Comment thread mcpjam-inspector/forge.config.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 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 `@mcpjam-inspector/forge.config.ts`:
- Around line 202-209: Move the temporary directory creation and the asar
extraction into the guarded try/finally so tmpDir is always cleaned up on
failure: create tmpDir with mkdirSync and call asar.extractAll(asarPath, tmpDir)
inside the try block (not before it) and remove the unnecessary await on
asar.extractAll since it is synchronous; keep the finally block to delete tmpDir
and preserve the existing succeeded flag handling around the extraction and
rebuild steps.
- Around line 185-225: The postPackage hook is rebuilding app.asar after signing
and integrity embedding (breaking EnableEmbeddedAsarIntegrityValidation /
OnlyLoadAppFromAsar and code signatures); remove the asar extract/recreate logic
from the postPackage function and either (a) add
`@electron-forge/plugin-auto-unpack-natives` to the Forge config so unpack:
"*.node" is applied before signing, or (b) move the current asar unpack/repack
steps from the postPackage hook into the afterPack hook so the archive is
rebuilt prior to integrity embedding/signing (update/remove the code inside
postPackage and relocate the
renameSync/asar.extractAll/asar.createPackageWithOptions/unlinkSync/rmSync logic
accordingly).
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d18b34fb-e400-4a86-a475-6d81e9662deb

📥 Commits

Reviewing files that changed from the base of the PR and between d40074d and 3d2f2db.

📒 Files selected for processing (1)
  • mcpjam-inspector/forge.config.ts

Comment thread mcpjam-inspector/forge.config.ts Outdated
Comment thread mcpjam-inspector/forge.config.ts Outdated
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. labels May 8, 2026
…ore asarApp

The postPackage hook was added to ensure .node files end up in
app.asar.unpacked, but it is unnecessary: afterCopy fires inside
electron-packager's copyTemplate() step, which runs before asarApp()
creates the archive. So packagerConfig.asar.unpack: "*.node" already
sees the @ngrok binaries that afterCopy placed in .vite/build/node_modules
and marks them as unpacked at asar creation time.

Verified empirically: bypassing the postPackage body with `continue`
still produced app.asar.unpacked/.vite/build/node_modules/@ngrok/.../*.node.

Removes the @electron/asar import and the hooks block entirely.
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels May 8, 2026
@bsradcliffe
Copy link
Copy Markdown
Author

@coderabbitai resume

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 8, 2026

Tip

For best results, initiate chat on the files or code changes.

@bsradcliffe: Resuming reviews on this PR. 🐇

[resume]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] App fails to launch, missing modules?

2 participants