Skip to content

resources/node_modules/wmi-client is missing from packaged builds, so the WMIC path never loads #1302

Description

@rafaelcaraballo-habi

Summary

In the installed v1.17.2 build, resources/node_modules/wmi-client does not exist, so getWMIC() always throws MODULE_NOT_FOUND and permanently sets wmicUnavailable = true. The WMIC path is effectively dead for every user of the packaged app.

On machines where wmi-bridge also returns nothing, that leaves no working path at all for the internal laptop panel, and it is never assigned type: "wmi".

The require path

src/Monitors.js:1174-1175:

let path = process.argv.find((val) => { return (val.indexOf("--apppath=") >= 0) }).substring(10)
WmiClient = require(require('path').join(path, '../node_modules/wmi-client'));

--apppath is app.getAppPath(). Taken from the actual running child process on my machine:

"...\twinkle-tray\Twinkle Tray.exe" ...\resources\app.asar\src\Monitors.js
  --isdev=false --apppath=C:\...\twinkle-tray\resources\app.asar --skiptest=true

So the computed path is resources\node_modules\wmi-client. That matches what extraResources in package.json:133-139 is supposed to produce:

"extraResources": [
  "node_modules\\wmi-client\\lib\\*",
  "node_modules\\wmi-client\\scripts\\*",
  "node_modules\\wmi-client\\index.js",
  "node_modules\\wmi-client\\package.json",
  "node_modules\\wmi-client\\.jscsrc"
],

The code is right — the files just aren't there. On a normal NSIS install, resources/ contains only:

app.asar
app.asar.unpacked/
app-update.yml
elevate.exe

No node_modules directory. wmi-client is present only at resources/app.asar.unpacked/node_modules/wmi-client (from asarUnpack).

Verified with the app's own Electron runtime (ELECTRON_RUN_AS_NODE=1):

ruta calculada: ...\resources\node_modules\wmi-client
require FALLO: MODULE_NOT_FOUND

I don't know why asarUnpack emitted and extraResources didn't — both use backslash globs — so I'd treat the packaging step as the thing to check rather than the pattern itself.

Requiring it from inside the asar isn't sufficient

Pointing the require at app.asar/node_modules/wmi-client loads the module, but the query then fails:

Error: spawn wmic ENOENT
  spawnargs: [
    '/NAMESPACE:\\\\root\\WMI', '/NODE:localhost', 'path', 'WmiMonitorBrightness',
    'get', '*', "/FORMAT:'lib/csv.xsl':'delimiter=^@^'"
  ]

wmic itself resolves fine from that same process — spawnSync("wmic", ...) returns CurrentBrightness=49. The ENOENT is the cwd: wmi-client runs with its module directory as cwd so it can reach lib/csv.xsl relatively, and a path inside an asar archive is not a real directory, so the spawn fails. The module has to live on the real filesystem.

Workaround that fixes it

Recreating the layout the code expects:

New-Item -ItemType Junction `
  -Path "$env:LOCALAPPDATA\Programs\twinkle-tray\resources\node_modules\wmi-client" `
  -Target "$env:LOCALAPPDATA\Programs\twinkle-tray\resources\app.asar.unpacked\node_modules\wmi-client"

After that, the same require succeeds and the query returns real data:

require via '../node_modules/wmi-client': OK
err: null
result: [{"Active":true,"CurrentBrightness":49,
          "InstanceName":"DISPLAY\\BOE0A21\\4&32d03ed7&0&UID8388688_0","Levels":101}]

And the internal panel is detected correctly:

before:  Display 1  type=none
after:   Display 1  type=wmi

Three related robustness issues

These are what made the failure silent rather than diagnosable.

1. getBrightnessWMI() throws on an empty response. On my machine the native bridge loads but getBrightness() returns {} (so does getMonitors()). At Monitors.js:791, {} has no .failed, so it takes the success branch and calls readInstanceName(undefined), which returns undefined, and hwid[2] throws. It ends up at the generic catch and resolves false — correct outcome, but by exception rather than by the failed check. A guard on monitor.InstanceName would make this explicit.

2. wmi-bridge-test.js reports ok for an empty result. It only rejects when wmiMonitors.failed is set. With {}, the for loop body never runs and it resolves normally, so wmiBridgeOK becomes true and canUseWmiBridge is set even though the bridge returned no monitors.

3. Malformed PowerShell fallback. Monitors.js:953:

exec(`powershell.exe -NoProfile (Get-WmiObject -Namespace root\\wmi -Class WmiMonitorBrightnessMethods).wmisetbrightness(0, ${brightness})"`)

There's an unbalanced trailing " and no -Command, so this can't succeed if it's ever reached.

Worth noting the native bridge's write path is fine — wmibridge.setBrightness(80) genuinely changed the panel. Only the reads came back empty.

Environment

  • Twinkle Tray v1.17.2 (NSIS install)
  • Windows 11 Pro 26200
  • Internal panel BOE0A21; WmiMonitorBrightness and WmiMonitorBrightnessMethods both present and Active=TRUE
  • WMIC present at C:\Windows\System32\wbem\WMIC.exe, Wbem on PATH

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions