KISS/DRY sweep — drops ~90 LOC with no behavior change. - circularBuffer: drop unused pushAll/toArray/size; fold toArray into drain - gracefulExit: inline Cleanup type + failsafe const; signal→code as a record instead of nested ternary; drop dead .catch on Promise.allSettled; drop unused forceExit - memory: inline heapDumpRoot() + writeSnapshot() (single-use); collapse the two fd/smaps try/catch blocks behind one `swallow` helper; build potentialLeaks functionally (array+filter) instead of imperative push-chain; UNITS at file bottom - memoryMonitor: inline DEFAULTS; drop unused onSnapshot; collapse dumpedHigh/dumpedCritical bools to a single Set; single callback dispatch line instead of duplicated if-chains - entry.tsx: factor `dumpNotice` formatter (used twice by onHigh + onCritical) - useMainApp resize debounce: drop redundant `if (timer)` guards (clearTimeout(undefined) is a no-op); init as undefined not null - useVirtualHistory: trim wall-of-text comment to one-line intent; hoist `const n = items.length`; split comma-declared lets; remove the `;[start, end] = frozenRange` destructure in favor of direct Math.min clamps; hoist `hi` init in upperBound for consistency Validation: tsc clean (both configs), eslint clean on touched files, vitest 102/102, build produces shebang-preserved dist/entry.js, performHeapDump smoke-test still writes valid snapshot + diagnostics.
50 lines
1.6 KiB
JavaScript
50 lines
1.6 KiB
JavaScript
#!/usr/bin/env -S node --max-old-space-size=8192 --expose-gc
|
|
import { bootBanner } from './bootBanner.js'
|
|
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'
|
|
|
|
if (!process.stdin.isTTY) {
|
|
console.log('hermes-tui: no TTY')
|
|
process.exit(0)
|
|
}
|
|
|
|
process.stdout.write(bootBanner())
|
|
|
|
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: [() => 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 => process.stderr.write(`hermes-tui: received ${signal}\n`)
|
|
})
|
|
|
|
const stopMemoryMonitor = startMemoryMonitor({
|
|
onCritical: (snap, dump) => {
|
|
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 [{ render }, { App }] = await Promise.all([import('@hermes/ink'), import('./app.js')])
|
|
|
|
render(<App gw={gw} />, { exitOnCtrlC: false })
|