Files
hermes-agent/apps
g3org3yo 6c3d4a4af7 fix(desktop): resolve plugin SDK namespaces lazily, not at module scope
runtime.ts captured the SDK namespaces (the plugin SDK, React and the two jsx
runtimes) in a module-scope object literal, and that module sits in an import
cycle: sdk/index -> @/contrib/* -> contrib/runtime-loader -> sdk/runtime ->
sdk/index.

In an unbundled (dev) graph the namespace object is live, so the capture works.
In a production bundle the bundler emits the SDK namespace as a hoisted `var`
whose assignment lands AFTER the literal that reads it, so the captured value
is `undefined` (no TDZ error) and `Object.keys(GLOBALS[globalKey])` in
`shimUrl()` throws "Cannot convert undefined or null to object".

That throw happens inside `loadRuntimePlugin()` -- via `unsupportedImports()`
-> `sdkImportMap()` -> `shimUrl()`, which run for every source before it is
evaluated -- so it is content-independent: EVERY plugin loaded from
$HERMES_HOME/desktop-plugins/<id>/plugin.js (and the desktop/plugin.js half of
a unified package) fails to load, showing status "failed" in
Capabilities -> Plugins.

Resolve the namespaces at call time instead: `installPluginSdk()` and the shim
builder only ever run once the app is up, so reading them there is always safe
and statement ordering can no longer matter.

Repro (production build only): build apps/desktop for production, drop any
plugin.js into ~/.hermes/desktop-plugins/<id>/, start the app ->
[plugins] runtime load failed (<id>) TypeError: Cannot convert undefined or
null to object (.../assets/sdk-<hash>.js:5).

Note: a vitest unit test cannot catch this (dev module graph keeps the
namespace live); the faithful guard is a production-build smoke test that
loads a fixture plugin through loadRuntimePlugin().
2026-09-10 15:35:31 -07:00
..