mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-03 02:11:48 +00:00
82 lines
2.5 KiB
JavaScript
82 lines
2.5 KiB
JavaScript
#!/usr/bin/env -S node --max-old-space-size=8192 --expose-gc
|
|
// Must be first import. If the user explicitly opts into truecolor, this
|
|
// nudges chalk / supports-color before either package is initialized.
|
|
import './lib/forceTruecolor.js'
|
|
|
|
import type { FrameEvent } from '@hermes/ink'
|
|
|
|
import { GatewayClient } from './gatewayClient.js'
|
|
import { setupGracefulExit } from './lib/gracefulExit.js'
|
|
import { formatBytes, type HeapDumpResult, performHeapDump } from './lib/memory.js'
|
|
import { type MemorySnapshot, startMemoryMonitor } from './lib/memoryMonitor.js'
|
|
import { resetTerminalModes } from './lib/terminalModes.js'
|
|
|
|
if (!process.stdin.isTTY) {
|
|
console.log('hermes-tui: no TTY')
|
|
process.exit(0)
|
|
}
|
|
|
|
// Start from a clean slate. If a previous TUI crashed or was kill -9'd, the
|
|
// terminal tab can still have mouse/focus/paste modes enabled.
|
|
resetTerminalModes()
|
|
|
|
const gw = new GatewayClient()
|
|
|
|
gw.start()
|
|
|
|
const dumpNotice = (snap: MemorySnapshot, dump: HeapDumpResult | null) =>
|
|
`hermes-tui: ${snap.level} memory (${formatBytes(snap.heapUsed)}) — auto heap dump → ${dump?.heapPath ?? '(failed)'}\n`
|
|
|
|
setupGracefulExit({
|
|
cleanups: [
|
|
() => {
|
|
resetTerminalModes()
|
|
|
|
return gw.kill()
|
|
}
|
|
],
|
|
onError: (scope, err) => {
|
|
const message = err instanceof Error ? `${err.name}: ${err.message}` : String(err)
|
|
|
|
process.stderr.write(`hermes-tui ${scope}: ${message.slice(0, 2000)}\n`)
|
|
},
|
|
onSignal: signal => {
|
|
resetTerminalModes()
|
|
process.stderr.write(`hermes-tui: received ${signal}\n`)
|
|
}
|
|
})
|
|
|
|
const stopMemoryMonitor = startMemoryMonitor({
|
|
onCritical: (snap, dump) => {
|
|
resetTerminalModes()
|
|
process.stderr.write(dumpNotice(snap, dump))
|
|
process.stderr.write('hermes-tui: exiting to avoid OOM; restart to recover\n')
|
|
process.exit(137)
|
|
},
|
|
onHigh: (snap, dump) => process.stderr.write(dumpNotice(snap, dump))
|
|
})
|
|
|
|
if (process.env.HERMES_HEAPDUMP_ON_START === '1') {
|
|
void performHeapDump('manual')
|
|
}
|
|
|
|
process.on('beforeExit', () => stopMemoryMonitor())
|
|
|
|
const [ink, { App }, { logFrameEvent }, { trackFrame }] = await Promise.all([
|
|
import('@hermes/ink'),
|
|
import('./app.js'),
|
|
import('./lib/perfPane.js'),
|
|
import('./lib/fpsStore.js')
|
|
])
|
|
|
|
// Both consumers are undefined when their env flags are off; only attach
|
|
// onFrame when at least one is on so ink skips timing in the default case.
|
|
const onFrame =
|
|
logFrameEvent || trackFrame
|
|
? (event: FrameEvent) => {
|
|
logFrameEvent?.(event)
|
|
trackFrame?.(event.durationMs)
|
|
}
|
|
: undefined
|
|
|
|
ink.render(<App gw={gw} />, { exitOnCtrlC: false, onFrame })
|