fix(desktop): explain unavailable Windows ARM64 window context

This commit is contained in:
itsflownium
2026-09-05 19:37:13 +10:00
committed by brooklyn!
parent 067fb583be
commit 9b363a33db
3 changed files with 64 additions and 2 deletions

View File

@@ -1,6 +1,12 @@
import { describe, expect, it } from 'vitest'
import { type EnumeratedWindow, enumerationFailureNote, pickWindowBelow, resolveOutsideAsar } from './window-below'
import {
type EnumeratedWindow,
enumerationFailureNote,
getWindowsFailureReason,
pickWindowBelow,
resolveOutsideAsar
} from './window-below'
const win = (pid: number, x = 0, y = 0, width = 800, height = 600, app = `app-${pid}`): EnumeratedWindow => ({
app,
@@ -146,6 +152,32 @@ describe('enumerationFailureNote', () => {
})
})
describe('getWindowsFailureReason', () => {
it('explains the native binding gap on Windows ARM64 without discarding the failure', () => {
for (const detail of [
'module missing',
'binding failed to load',
'the window enumerator returned no window list'
]) {
const reason = getWindowsFailureReason(detail, 'win32', 'arm64')
expect(reason).toContain(detail)
expect(reason).toMatch(/win32-arm64/)
expect(reason).toMatch(/native binding/)
expect(reason).toMatch(/x64/)
expect(enumerationFailureNote('win32', {}, reason)).toContain(reason)
for (const [platform, arch] of [
['win32', 'x64'],
['darwin', 'arm64'],
['linux', 'arm64']
]) {
expect(getWindowsFailureReason(detail, platform, arch)).toBe(detail)
}
}
})
})
describe('resolveOutsideAsar', () => {
// The helper binary get-windows execs cannot run from inside the archive
// (execFile on a path through app.asar fails ENOTDIR), so the import must

View File

@@ -145,6 +145,19 @@ export interface EnumerationFailure {
export const enumerationFailed = <T>(result: EnumerationFailure | T): result is EnumerationFailure =>
typeof result === 'object' && result !== null && 'reason' in result
// Keep the native provider's failure detail shared by the tool and HUD log.
export function getWindowsFailureReason(detail: string, platform: string, arch: string): string {
if (platform === 'win32' && arch === 'arm64') {
return (
`${detail}. Window context requires a working win32-arm64 native binding; ` +
'get-windows 9.3.0 does not ship one. Use the x64 desktop build under Windows emulation, ' +
'or a build with a matching native binding. This affects both read_window_below and HUD window context.'
)
}
return detail
}
const describeError = (error: unknown): string =>
error instanceof Error ? error.message : String(error ?? 'unknown error')
@@ -284,7 +297,11 @@ export async function enumerateWindowsFrontToBack(
selfPid: number,
titlesAvailable: boolean
): Promise<EnumeratedWindow[] | EnumerationFailure> {
return (await readHyprlandWindows(selfPid)) ?? (await enumerateViaGetWindows(titlesAvailable))
const result = (await readHyprlandWindows(selfPid)) ?? (await enumerateViaGetWindows(titlesAvailable))
return enumerationFailed(result)
? { reason: getWindowsFailureReason(result.reason, process.platform, process.arch) }
: result
}
export async function readWindowBelow(

View File

@@ -539,6 +539,19 @@ The remote gateway host is configured per [profile](./profiles.md), so each prof
### Troubleshooting
### Window context unavailable on Windows ARM64
The ARM64 desktop build's `get-windows` 9.3.0 dependency does not ship a
`win32-arm64` native binding. Without a working binding, `read_window_below`
and HUD window context cannot enumerate other apps' windows. The error and
HUD log include this limitation alongside the underlying failure reason.
Use the x64 desktop build under Windows emulation, or a custom build with a
matching native binding. Changing the agent backend or granting macOS screen
permissions cannot fix a missing Windows binding; enumeration runs on the
computer hosting the desktop app. This diagnostic does not add native ARM64
window enumeration support.
- **Sign-in fails with 401 / "Invalid credentials"** — the username or password doesn't match the backend's `HERMES_DASHBOARD_BASIC_AUTH_USERNAME` / `HERMES_DASHBOARD_BASIC_AUTH_PASSWORD`. The backend returns the same generic error for an unknown user and a wrong password (no enumeration oracle), so double-check both. Confirm the gate is on with `curl -s http://<host>:9119/api/status | jq '.auth_required, .auth_providers'` — it should report `true` and include `"basic"`.
- **No "Sign in" button — it asks for a session token instead** — the backend's username/password provider isn't active. `/api/status` won't list `"basic"` in `auth_providers`. Make sure both the username and a password (or password hash) are set in `~/.hermes/.env` and that the dashboard process actually loaded them.
- **Signed out on every restart** — set `HERMES_DASHBOARD_BASIC_AUTH_SECRET` to a stable value. Without it the token-signing key is regenerated per boot, invalidating all sessions.