Fix interactive input by using Console.readLine instead of Process.exec
Process.exec uses popen(cmd, "r") which creates a read-only pipe where the subprocess stdin is disconnected from the terminal. Console.readLine reads directly from the parent process stdin via fgets, fixing all interactive prompts (askConfirm, askInput, choice menus). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
26
cli/pal.lux
26
cli/pal.lux
@@ -128,10 +128,10 @@ fn printStatusLine(name: String, state: String, detail: String): Unit with {Proc
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ask for confirmation: returns true if user confirms
|
// Ask for confirmation: returns true if user confirms
|
||||||
fn askConfirm(question: String, defaultYes: Bool): Bool with {Process} = {
|
fn askConfirm(question: String, defaultYes: Bool): Bool with {Console, Process} = {
|
||||||
let prompt = if defaultYes then " [Y/n] " else " [y/N] "
|
let prompt = if defaultYes then " [Y/n] " else " [y/N] "
|
||||||
let ignore = Process.exec("printf '%s%s' \"" + question + "\" \"" + prompt + "\" >&2")
|
let ignore = Process.exec("printf '%s%s' \"" + question + "\" \"" + prompt + "\" >&2")
|
||||||
let response = String.trim(Process.exec("head -n1 </dev/tty"))
|
let response = String.trim(Console.readLine())
|
||||||
let lower = execQuiet("echo '" + response + "' | tr A-Z a-z")
|
let lower = execQuiet("echo '" + response + "' | tr A-Z a-z")
|
||||||
if String.length(lower) == 0 then
|
if String.length(lower) == 0 then
|
||||||
defaultYes
|
defaultYes
|
||||||
@@ -148,13 +148,13 @@ fn askConfirm(question: String, defaultYes: Bool): Bool with {Process} = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ask for text input with a default value
|
// Ask for text input with a default value
|
||||||
fn askInput(prompt: String, defaultVal: String): String with {Process} = {
|
fn askInput(prompt: String, defaultVal: String): String with {Console, Process} = {
|
||||||
let display = if String.length(defaultVal) > 0 then
|
let display = if String.length(defaultVal) > 0 then
|
||||||
"printf '%s [%s]: ' \"" + prompt + "\" \"" + defaultVal + "\" >&2"
|
"printf '%s [%s]: ' \"" + prompt + "\" \"" + defaultVal + "\" >&2"
|
||||||
else
|
else
|
||||||
"printf '%s: ' \"" + prompt + "\" >&2"
|
"printf '%s: ' \"" + prompt + "\" >&2"
|
||||||
let ignore = Process.exec(display)
|
let ignore = Process.exec(display)
|
||||||
let input = String.trim(Process.exec("head -n1 </dev/tty"))
|
let input = String.trim(Console.readLine())
|
||||||
if String.length(input) == 0 then defaultVal else input
|
if String.length(input) == 0 then defaultVal else input
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,7 +302,7 @@ fn showMiniLogo(): Unit with {Process} = {
|
|||||||
// Welcome flow (Interactive)
|
// Welcome flow (Interactive)
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
fn showInteractiveWelcome(): Unit with {Process} = {
|
fn showInteractiveWelcome(): Unit with {Console, Process} = {
|
||||||
print("")
|
print("")
|
||||||
showLogo()
|
showLogo()
|
||||||
print("")
|
print("")
|
||||||
@@ -325,7 +325,7 @@ fn showInteractiveWelcome(): Unit with {Process} = {
|
|||||||
print("")
|
print("")
|
||||||
|
|
||||||
let ignore8 = Process.exec("printf 'Choice [1]: ' >&2")
|
let ignore8 = Process.exec("printf 'Choice [1]: ' >&2")
|
||||||
let choice = String.trim(Process.exec("head -n1 </dev/tty"))
|
let choice = String.trim(Console.readLine())
|
||||||
|
|
||||||
match choice {
|
match choice {
|
||||||
"2" => {
|
"2" => {
|
||||||
@@ -765,7 +765,7 @@ fn doServerUnmount(): Unit with {Process} = {
|
|||||||
// Export/Import
|
// Export/Import
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
fn doExport(): Unit with {Process} = {
|
fn doExport(): Unit with {Console, Process} = {
|
||||||
print("")
|
print("")
|
||||||
let timestamp = execQuiet("date +%Y-%m-%d")
|
let timestamp = execQuiet("date +%Y-%m-%d")
|
||||||
let exportFile = "pal-export-" + timestamp + ".tar.zst"
|
let exportFile = "pal-export-" + timestamp + ".tar.zst"
|
||||||
@@ -790,7 +790,7 @@ fn doExport(): Unit with {Process} = {
|
|||||||
print("")
|
print("")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doImport(archivePath: String): Unit with {Process} = {
|
fn doImport(archivePath: String): Unit with {Console, Process} = {
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
if String.length(archivePath) == 0 then {
|
if String.length(archivePath) == 0 then {
|
||||||
@@ -974,7 +974,7 @@ fn showCompactStatus(): Unit with {Process} = {
|
|||||||
// Health check (default command)
|
// Health check (default command)
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
fn showHealthCheck(): Unit with {Process} = {
|
fn showHealthCheck(): Unit with {Console, Process} = {
|
||||||
// Check if this is an interactive terminal
|
// Check if this is an interactive terminal
|
||||||
let isTty = execQuiet("test -t 0 && echo yes || echo no")
|
let isTty = execQuiet("test -t 0 && echo yes || echo no")
|
||||||
|
|
||||||
@@ -1225,7 +1225,7 @@ fn showOnboardSummary(deviceName: String): Unit with {Process} = {
|
|||||||
print("")
|
print("")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doOnboardSteps(): Unit with {Process} = {
|
fn doOnboardSteps(): Unit with {Console, Process} = {
|
||||||
// ── Step 1: Device ──────────────────────────────────────────────────
|
// ── Step 1: Device ──────────────────────────────────────────────────
|
||||||
let ignore = Process.exec("printf '\\033[1m── Step 1: Device ──\\033[0m\\n\\n' >&2")
|
let ignore = Process.exec("printf '\\033[1m── Step 1: Device ──\\033[0m\\n\\n' >&2")
|
||||||
|
|
||||||
@@ -1273,7 +1273,7 @@ fn doOnboardSteps(): Unit with {Process} = {
|
|||||||
print("")
|
print("")
|
||||||
|
|
||||||
let ignore8 = Process.exec("printf 'Choice [3]: ' >&2")
|
let ignore8 = Process.exec("printf 'Choice [3]: ' >&2")
|
||||||
let configChoice = String.trim(Process.exec("head -n1 </dev/tty"))
|
let configChoice = String.trim(Console.readLine())
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
match configChoice {
|
match configChoice {
|
||||||
@@ -1451,7 +1451,7 @@ fn doOnboardSteps(): Unit with {Process} = {
|
|||||||
} else ()
|
} else ()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doOnboard(): Unit with {Process} = {
|
fn doOnboard(): Unit with {Console, Process} = {
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
if isInitialized() then {
|
if isInitialized() then {
|
||||||
@@ -1620,7 +1620,7 @@ fn showServerHelp(): Unit with {Process} = {
|
|||||||
// Main
|
// Main
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
fn main(): Unit with {Process} = {
|
fn main(): Unit with {Console, Process} = {
|
||||||
let args = Process.args()
|
let args = Process.args()
|
||||||
let cmd = match List.get(args, 1) {
|
let cmd = match List.get(args, 1) {
|
||||||
Some(c) => c,
|
Some(c) => c,
|
||||||
|
|||||||
Reference in New Issue
Block a user