Repository navigation
hub-ui-onboard: default terminal confirmation is unusable under Nuxt 5's nuxi #448
Description
Summary
Since v1.2.5 (#445), the default onConfirm of @devframes/hub-ui-onboard (confirmInTerminal() on process.stdin) asks before each panel Install. It never installs without consent, but under Nuxt 5's CLI the developer often can't see the question, or can't answer it. #445 expected that "nuxi forks the dev server with an inherited stdin, so the default prompt is expected to work there". That is not what happens with @nuxt/cli-nightly 4.0.1.
What nuxi does
- The first dev server runs inside the nuxi process. Restarts move it to a pre-warmed fork (
ForkPoolin@nuxt/cli-nightly/dist/dev-*.mjs). - In the interactive TUI (the default in a terminal of at least 40x10), the nuxi process holds stdin in raw mode for single-key shortcuts. It captures the dev server's logs, and forks get
stdio: ['ignore', 'pipe', 'pipe', 'ipc']. - In plain mode (
NUXT_TUI=plain, or a small terminal), the nuxi process reads stdin with areadlinefor line shortcuts (r,o,u,qr,y= copy URL,c,q,h). Forks inherit the TTY.
Reproduction
nuxi dev (Nuxt 5 nightly, @nuxt/cli-nightly 4.0.1) under a 120x40 PTY, on a project where @nuxt/devtools-onboard calls createOnboarding() without onConfirm. Install is triggered with a same-origin POST /__devtools/__onboard/install.
| nuxi mode | Dev server runs in | What happens on Install |
|---|---|---|
| TUI, first boot | nuxi process (stdin raw and flowing, owned by the TUI) | DF9005 is folded into the TUI's log capture. The developer only sees "⚠ N warnings". The next keypress answers it as a raw chunk, so pressing w or l to read the warning refuses the install (403). |
| TUI, after any restart | fork, stdin /dev/null |
DF9006, refused at once (403 in 23 ms). Install can never succeed after the first restart. |
| plain, first boot | nuxi process | Works, but y⏎ also runs nuxi's own y shortcut (copy server URL) |
| plain, after a restart | fork that shares the TTY with the parent's readline |
The parent reads the line, so once('data') never fires. The POST and the shared asking promise stay open forever, and the panel stays on "Confirm in your terminal...". |
Possible directions
- When the input is a TTY that something else holds in raw mode (
input.isRaw === true), refuse at once with a clear diagnostic. That beats letting an unrelated keypress answer a question nobody can see. - Don't leave a question that can never be answered open forever: for example, refuse after a bounded wait, so the panel shows
messages.refused. - Fix the nuxi claim in the guide, and recommend that hosts whose CLI owns the terminal pass
onConfirm(ideally something nuxi provides that routes the prompt through its TUI).
Downstream: nuxt/devtools#1126 upgrades to v1.2.5, keeps the default onConfirm (fails closed), and lists this as a known gap. refs nuxt/nuxt#36581
This issue was written with the help of an AI agent.
Activity
antfubot commentedon Oct 14, 2026
Update: nuxt/devtools#1126 no longer keeps the default.
@nuxt/devtools-onboardnow passes its ownonConfirm: () => truebehind a middleware that only lets through requests from this machine: a loopback socket and a localHostname. Other devices get 403. The terminal prompt under nuxi is still the problem described above for any host that relies on the default.Written with the help of an AI agent.
antfubot commentedon Oct 20, 2026
Correction to my last comment: nuxt/devtools#1126 now keeps your security line. Its
onConfirmis the same terminal question, adapted to nuxi:yallows and any other key refuses.yes⏎, so nuxi'syshortcut doesn't also fire.The same-machine check is gone.
Written with the help of an AI agent.