Bug Description
The memos-local-hermes-plugin bridge fails to start on Node.js v22.x with the error:
bridge: fatal: Cannot read properties of undefined (reading 'exports')
Memory Viewer not ready after 120s
Environment
- Node.js: v22.22.2 (also reproduces on v22.x)
- Platform: macOS
- Plugin version: 2.0.2 (also affects latest main)
- Hermes Agent via Python adapter (
bridge_client.py)
Root Cause
Two compounding bugs in bridge.cts / dist/bridge.cjs:
Bug 1: Incorrect __dirname in compiled output
The source file bridge.cts uses __dirname computed at compile time (referencing the project root), but after TypeScript compilation the emitted dist/bridge.cjs runs with its own __dirname pointing to dist/. This causes all relative path imports to resolve to non-existent .ts files:
// In dist/bridge.cjs (compiled output)
const pipelinePath = path.resolve(__dirname, "core/pipeline/index.ts");
// __dirname === "/Users/.../memos-plugin/dist"
// pipelinePath === "/Users/.../memos-plugin/dist/core/pipeline/index.ts" ← .ts file, doesn't exist!
Bug 2: Using require(file://URL) for ESM modules
Even when the .ts → .js path issue is manually patched, dist/bridge.cjs uses require(file://.../dist/core/pipeline/index.js) to load ESM modules. Node.js CJS mode does not support loading file:// protocol URLs via require() — this is not a valid module resolution strategy in Node.js.
Error Trace
bridge: fatal: Cannot read properties of undefined (reading 'exports')
at load (node:internal/modules/cjs/loader:1508:31)
at Module._resolveFilename [as .resolve] (node:internal/modules/cjs/loader:1483:9)
at Module.load (node:internal/modules/cjs/loader:1536:9)
at module._load (node:internal/modules/cjs/loader:1637:7)
at Module.load (node:internal/modules/cjs/loader:1634:9)
at node:internal/modules/esm loader:2349:30
at async node:internal/modules/esm/loader:2509:12
at async node:internal/modules/esm/loader:2673:7
...
Reproduction Steps
- Install memos-local-hermes-plugin on Node.js v22.x
- Start Hermes Agent (which triggers
bridge_client.py)
- Observe timeout waiting for Memory Viewer at port 18800
Workaround Applied
Created a pure ESM entrypoint run-bridge.mjs that:
- Uses
import.meta.url + fileURLToPath for correct __dirname resolution
- Uses native
import() for ESM module loading instead of require(file://)
- Modified
bridge_client.py to launch run-bridge.mjs instead of bridge.cts via tsx
Suggested Fix (upstream)
Option A — Fix bridge.cts source:
- Replace
path.resolve(__dirname, "...") with import.meta.url based resolution
- Change module loading from
require(file://) to dynamic import()
Option B — Emit pure ESM instead of CJS:
- The
package.json has "type": "module" — the project is ESM-first
- The
dist/bridge.cjs design (CJS wrapper loading ESM) is fundamentally incompatible with Node.js v22's strict ESM/CJS boundary enforcement
Option C — Simplify the bridge entry point:
- Remove the complex CJS→ESM bridge pattern entirely
- Use a plain
.mjs file as the direct daemon entry point
Impact
Any user running Node.js v22+ is completely unable to use the Hermes memory plugin, as the bridge process fails to start before any memory operations can occur.
Tags
bug hermes nodejs-v22 module-resolution CJS/ESM
Bug Description
The memos-local-hermes-plugin bridge fails to start on Node.js v22.x with the error:
Environment
bridge_client.py)Root Cause
Two compounding bugs in
bridge.cts/dist/bridge.cjs:Bug 1: Incorrect
__dirnamein compiled outputThe source file
bridge.ctsuses__dirnamecomputed at compile time (referencing the project root), but after TypeScript compilation the emitteddist/bridge.cjsruns with its own__dirnamepointing todist/. This causes all relative path imports to resolve to non-existent.tsfiles:Bug 2: Using
require(file://URL)for ESM modulesEven when the
.ts→.jspath issue is manually patched,dist/bridge.cjsusesrequire(file://.../dist/core/pipeline/index.js)to load ESM modules. Node.js CJS mode does not support loadingfile://protocol URLs viarequire()— this is not a valid module resolution strategy in Node.js.Error Trace
Reproduction Steps
bridge_client.py)Workaround Applied
Created a pure ESM entrypoint
run-bridge.mjsthat:import.meta.url+fileURLToPathfor correct__dirnameresolutionimport()for ESM module loading instead ofrequire(file://)bridge_client.pyto launchrun-bridge.mjsinstead ofbridge.ctsvia tsxSuggested Fix (upstream)
Option A — Fix
bridge.ctssource:path.resolve(__dirname, "...")withimport.meta.urlbased resolutionrequire(file://)to dynamicimport()Option B — Emit pure ESM instead of CJS:
package.jsonhas"type": "module"— the project is ESM-firstdist/bridge.cjsdesign (CJS wrapper loading ESM) is fundamentally incompatible with Node.js v22's strict ESM/CJS boundary enforcementOption C — Simplify the bridge entry point:
.mjsfile as the direct daemon entry pointImpact
Any user running Node.js v22+ is completely unable to use the Hermes memory plugin, as the bridge process fails to start before any memory operations can occur.
Tags
bughermesnodejs-v22module-resolutionCJS/ESM