refactor(tui): reuse DASHBOARD_TUI_MODE for hosted /exit guard

Follow-up to the salvaged hosted /exit fix. Instead of a separate 4-env-var
fingerprint (HERMES_TUI_INLINE + /opt/data HERMES_HOME + HERMES_WRITE_SAFE_ROOT
+ HERMES_DISABLE_LAZY_INSTALLS), gate /exit and /quit on the existing
DASHBOARD_TUI_MODE flag (HERMES_TUI_DASHBOARD) that the keyboard idle-exit
(useInputHandlers) and SIGINT-ignore (entry.tsx) paths already use. One hosted
detection mechanism instead of two divergent ones.

Extract the refusal text to an exported DASHBOARD_EXIT_DISABLED_MESSAGE so the
test asserts the same source of truth as production (no change-detector on the
literal). Test mocks only the DASHBOARD_TUI_MODE export via importActual so the
other env exports stay real.
This commit is contained in:
kshitijk4poor 2026-06-19 12:29:19 +05:30 committed by kshitij
parent 15e3b64b75
commit 3f0e9849e7
2 changed files with 34 additions and 31 deletions

View file

@ -1,6 +1,6 @@
import { forceRedraw, type MouseTrackingMode } from '@hermes/ink'
import { NO_CONFIRM_DESTRUCTIVE } from '../../../config/env.js'
import { DASHBOARD_TUI_MODE, NO_CONFIRM_DESTRUCTIVE } from '../../../config/env.js'
import { dailyFortune, randomFortune } from '../../../content/fortunes.js'
import { HOTKEYS } from '../../../content/hotkeys.js'
import { isSectionName, nextDetailsMode, parseDetailsMode, SECTION_NAMES } from '../../../domain/details.js'
@ -76,19 +76,10 @@ const DETAILS_USAGE =
const DETAILS_SECTION_USAGE = 'usage: /details <section> [hidden|collapsed|expanded|reset]'
const truthyEnv = (v?: string) => /^(?:1|true|yes|on)$/i.test((v ?? '').trim())
const hostedInlineDashboardChat = () => {
const hermesHome = (process.env.HERMES_HOME ?? '').trim()
const hostedHome = hermesHome === '/opt/data' || hermesHome.startsWith('/opt/data/')
return (
process.env.HERMES_TUI_INLINE === '1' &&
hostedHome &&
process.env.HERMES_WRITE_SAFE_ROOT === '/opt/data' &&
truthyEnv(process.env.HERMES_DISABLE_LAZY_INSTALLS)
)
}
// Shown when /exit or /quit is refused in the hosted dashboard chat. Kept as a
// constant so the test asserts against the same source of truth as production.
export const DASHBOARD_EXIT_DISABLED_MESSAGE =
'exit is disabled in hosted dashboard chat — use /new to start a fresh session'
export const coreCommands: SlashCommand[] = [
{
@ -128,8 +119,15 @@ export const coreCommands: SlashCommand[] = [
help: 'exit hermes',
name: 'quit',
run: (_arg, ctx) => {
if (hostedInlineDashboardChat()) {
ctx.transcript.sys('exit is disabled in hosted dashboard chat — use /new to start a fresh session')
// In the hosted dashboard chat there is no in-page restart path after
// the PTY child exits, so quitting bricks the tab until a refresh. The
// keyboard idle-exit (Ctrl+C / Ctrl+D) and SIGINT handling already refuse
// to die in this mode (see useInputHandlers + entry.tsx); gate /exit and
// /quit on the same DASHBOARD_TUI_MODE flag. Unlike the keyboard path
// (which auto-starts a fresh chat), the explicit quit command refuses and
// instructs the user to run /new themselves.
if (DASHBOARD_TUI_MODE) {
ctx.transcript.sys(DASHBOARD_EXIT_DISABLED_MESSAGE)
return
}