mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-01 12:02:05 +00:00
main (cb982ad99) wired windows_hide_flags() into the auxiliary git/gh/wmic/
bash/powershell/taskkill legs but left two it didn't reach, plus the Electron
backend-launch leg it explicitly deferred. Cover them the same way:
- apps/desktop/electron/main.cjs: getNoConsoleVenvPython resolves the BASE
pythonw.exe instead of the venv Scripts\pythonw.exe shim, which re-execs a
console python.exe and flashes a conhost the desktop backend can't suppress.
Both backend creators put the venv site-packages on PYTHONPATH so imports
still resolve under the base interpreter. (main's commit said this Electron
leg "needs a Windows-tested change of its own".)
- tools/tts_tool.py, tools/transcription_tools.py, plugins/platforms/discord:
ffmpeg conversions (voice notes / TTS / STT) via windows_hide_flags().
- plugins/platforms/whatsapp: netstat + taskkill bridge-port cleanup via
windows_hide_flags().
All no-ops on POSIX. Tests assert the base-pythonw preference and the ffmpeg
legs pass CREATE_NO_WINDOW.
88 lines
4 KiB
JavaScript
88 lines
4 KiB
JavaScript
'use strict'
|
|
|
|
const test = require('node:test')
|
|
const assert = require('node:assert/strict')
|
|
const fs = require('node:fs')
|
|
const path = require('node:path')
|
|
|
|
const ELECTRON_DIR = __dirname
|
|
|
|
function readElectronFile(name) {
|
|
return fs.readFileSync(path.join(ELECTRON_DIR, name), 'utf8').replace(/\r\n/g, '\n')
|
|
}
|
|
|
|
function requireHiddenChildOptions(source, needle) {
|
|
const match = needle instanceof RegExp ? needle.exec(source) : null
|
|
const index = needle instanceof RegExp ? (match?.index ?? -1) : source.indexOf(needle)
|
|
assert.notEqual(index, -1, `missing call site: ${needle}`)
|
|
const snippet = source.slice(index, index + 700)
|
|
assert.match(
|
|
snippet,
|
|
/hiddenWindowsChildOptions\(/,
|
|
`expected ${needle} to wrap child-process options with hiddenWindowsChildOptions`
|
|
)
|
|
}
|
|
|
|
test('desktop background child processes opt into hidden Windows consoles', () => {
|
|
const source = readElectronFile('main.cjs')
|
|
|
|
assert.match(source, /function hiddenWindowsChildOptions\(options = \{\}\)/)
|
|
|
|
requireHiddenChildOptions(source, "execFileSync(\n 'reg'")
|
|
requireHiddenChildOptions(source, /execFileSync\(\s*pyExe/)
|
|
requireHiddenChildOptions(source, /spawn\(\s*resolveGitBinary\(\)/)
|
|
requireHiddenChildOptions(source, "execFileSync('taskkill'")
|
|
requireHiddenChildOptions(source, /spawn\(\s*command,\s*args/)
|
|
requireHiddenChildOptions(source, "spawn('curl'")
|
|
requireHiddenChildOptions(source, /spawn\(\s*backend\.command,\s*backend\.args/)
|
|
requireHiddenChildOptions(source, /hermesProcess = spawn\(\s*backend\.command,\s*backend\.args/)
|
|
requireHiddenChildOptions(source, /spawn\(\s*py,\s*\['-m', 'hermes_cli\.main', 'uninstall', '--gui-summary'\]/)
|
|
|
|
assert.match(source, /function unwrapWindowsVenvHermesCommand\(command, dashboardArgs\)/)
|
|
assert.match(source, /existing Hermes no-console Python at/)
|
|
assert.match(source, /function getNoConsoleVenvPython\(venvRoot\)/)
|
|
assert.match(source, /function toNoConsolePython\(pythonPath\)/)
|
|
assert.match(source, /function applyWindowsNoConsoleSpawnHints\(backend\)/)
|
|
assert.match(source, /function readVenvHome\(venvRoot\)/)
|
|
assert.match(source, /path\.join\(venvRoot, 'Scripts', 'pythonw\.exe'\)/)
|
|
assert.match(source, /backendStartFailure/)
|
|
assert.match(source, /HERMES_DESKTOP_READY_FILE/)
|
|
assert.match(source, /readyFile: true/)
|
|
assert.match(source, /function getVenvSitePackagesEntries\(venvRoot\)/)
|
|
assert.match(source, /path\.join\(venvRoot, 'Lib', 'site-packages'\)/)
|
|
assert.match(source, /args: \['-m', 'hermes_cli\.main', \.\.\.dashboardArgs\]/)
|
|
})
|
|
|
|
test('getNoConsoleVenvPython prefers base pythonw over the uv re-exec shim', () => {
|
|
const source = readElectronFile('main.cjs')
|
|
const body = source.slice(
|
|
source.indexOf('function getNoConsoleVenvPython(venvRoot)'),
|
|
source.indexOf('function getVenvSitePackagesEntries(venvRoot)')
|
|
)
|
|
|
|
// The venv Scripts\pythonw.exe re-execs a console python.exe (flashes a
|
|
// conhost); the base pythonw must be resolved first so it never runs.
|
|
const baseIdx = body.indexOf('basePythonw')
|
|
const shimIdx = body.indexOf("'Scripts', 'pythonw.exe'")
|
|
assert.notEqual(baseIdx, -1, 'base pythonw resolution missing')
|
|
assert.notEqual(shimIdx, -1, 'venv shim fallback missing')
|
|
assert.ok(baseIdx < shimIdx, 'base pythonw must be preferred before the venv Scripts shim')
|
|
})
|
|
|
|
test('intentional or interactive desktop child processes stay documented', () => {
|
|
const source = readElectronFile('main.cjs')
|
|
|
|
assert.match(source, /windowsHide: false/)
|
|
assert.match(source, /handOffWindowsBootstrapRecovery/)
|
|
assert.match(source, /'--repair', '--branch'/)
|
|
assert.match(source, /'--update', '--branch'/)
|
|
assert.match(source, /nodePty\.spawn\(command, args/)
|
|
assert.match(source, /spawn\('cmd\.exe', \['\/c', 'start'/)
|
|
})
|
|
|
|
test('bootstrap PowerShell runner hides Windows console children', () => {
|
|
const source = readElectronFile('bootstrap-runner.cjs')
|
|
|
|
assert.match(source, /function hiddenWindowsChildOptions\(options = \{\}\)/)
|
|
requireHiddenChildOptions(source, 'spawn(ps, fullArgs')
|
|
})
|