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
Summary
In the installed v1.17.2 build,
resources/node_modules/wmi-clientdoes not exist, sogetWMIC()always throwsMODULE_NOT_FOUNDand permanently setswmicUnavailable = true. The WMIC path is effectively dead for every user of the packaged app.On machines where
wmi-bridgealso returns nothing, that leaves no working path at all for the internal laptop panel, and it is never assignedtype: "wmi".The require path
src/Monitors.js:1174-1175:--apppathisapp.getAppPath(). Taken from the actual running child process on my machine:So the computed path is
resources\node_modules\wmi-client. That matches whatextraResourcesinpackage.json:133-139is supposed to produce:The code is right — the files just aren't there. On a normal NSIS install,
resources/contains only:No
node_modulesdirectory.wmi-clientis present only atresources/app.asar.unpacked/node_modules/wmi-client(fromasarUnpack).Verified with the app's own Electron runtime (
ELECTRON_RUN_AS_NODE=1):I don't know why
asarUnpackemitted andextraResourcesdidn'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-clientloads the module, but the query then fails:wmicitself resolves fine from that same process —spawnSync("wmic", ...)returnsCurrentBrightness=49. TheENOENTis the cwd:wmi-clientruns with its module directory as cwd so it can reachlib/csv.xslrelatively, 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:
After that, the same require succeeds and the query returns real data:
And the internal panel is detected correctly:
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 butgetBrightness()returns{}(so doesgetMonitors()). AtMonitors.js:791,{}has no.failed, so it takes the success branch and callsreadInstanceName(undefined), which returnsundefined, andhwid[2]throws. It ends up at the generic catch and resolvesfalse— correct outcome, but by exception rather than by thefailedcheck. A guard onmonitor.InstanceNamewould make this explicit.2.
wmi-bridge-test.jsreportsokfor an empty result. It only rejects whenwmiMonitors.failedis set. With{}, theforloop body never runs and it resolves normally, sowmiBridgeOKbecomestrueandcanUseWmiBridgeis set even though the bridge returned no monitors.3. Malformed PowerShell fallback.
Monitors.js:953: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
BOE0A21;WmiMonitorBrightnessandWmiMonitorBrightnessMethodsboth present andActive=TRUEC:\Windows\System32\wbem\WMIC.exe,WbemonPATH🤖 Generated with Claude Code