8.3 KiB
Shell Library (shlib) - TTY Wrappers
Purpose: Bash wrappers that overcome Nushell's TTY input limitations in execution stacks.
The Problem
When Nushell scripts call interactive programs (like TypeDialog) within execution stacks, TTY input handling fails:
# This doesn't work properly in Nushell execution stacks:
def run-interactive-form [] {
let result = (^typedialog form input.toml) # TTY issues
process_result $result
}
Why? Nushell's pipeline and execution stack architecture doesn't properly forward TTY file descriptors to child processes in all contexts.
The Solution
Bash wrappers handle TTY input, then pass results to Nushell via files:
┌─────────────────────────────────────────────────
────────────┐
│ User runs Nushell script │
└─────────────────┬───────────────────────────────
────────────┘
│
v
┌─────────────────────────────────────────────────
────────────┐
│ Nushell calls bash wrapper (shlib/*-tty.sh) │
└─────────────────┬───────────────────────────────
────────────┘
│
v
┌─────────────────────────────────────────────────
────────────┐
│ Bash wrapper handles TTY input (TypeDialog, prompts, etc) │
│ - Proper TTY file descriptor handling │
│ - Interactive input works correctly │
└─────────────────┬───────────────────────────────
────────────┘
│
v
┌─────────────────────────────────────────────────
────────────┐
│ Wrapper writes output to JSON file │
└─────────────────┬───────────────────────────────
────────────┘
│
v
┌─────────────────────────────────────────────────
────────────┐
│ Nushell reads JSON file (no TTY issues) │
│ - File-based IPC is reliable │
│ - No input stack problems │
└─────────────────────────────────────────────────
────────────┘
Naming Convention
Scripts in this directory follow the pattern: {operation}-tty.sh
{operation}: What the script does (e.g.,setup-wizard,auth-login)-tty: Indicates this is a TTY-handling wrapper.sh: Bash script extension
Examples:
setup-wizard-tty.sh- Setup wizard with TTY-safe inputauth-login-tty.sh- Authentication login with TTY-safe inputmfa-enroll-tty.sh- MFA enrollment with TTY-safe input
Current Wrappers
| Script | Purpose | TypeDialog Form |
|---|---|---|
setup-wizard-tty.sh |
Initial system setup configuration | .typedialog/core/forms/setup-wizard.toml |
auth-login-tty.sh |
User authentication login | .typedialog/core/forms/auth-login.toml |
mfa-enroll-tty.sh |
Multi-factor authentication enrollment | .typedialog/core/forms/mfa-enroll.toml |
Usage from Nushell
# Example: Run setup wizard from Nushell
def run-setup-wizard-interactive [] {
# Call bash wrapper (handles TTY properly)
let wrapper = "provisioning/core/shlib/setup-wizard-tty.sh"
let result = (bash $wrapper | complete)
if $result.exit_code == 0 {
# Read generated JSON (no TTY issues)
let config = (open provisioning/.typedialog/core/generated/setup-wizard.json | from json)
# Process config in Nushell
process_config $config
} else {
print "Setup wizard failed"
}
}
Usage from Bash/CLI
# Direct execution
./provisioning/core/shlib/setup-wizard-tty.sh
# With environment variable (backend selection)
TYPEDIALOG_BACKEND=web ./provisioning/core/shlib/auth-login-tty.sh
# With custom output location
OUTPUT_DIR=/tmp ./provisioning/core/shlib/mfa-enroll-tty.sh
Architecture Pattern
All wrappers follow this pattern:
-
Input Modes (fallback chain):
- TypeDialog interactive forms (if binary available)
- Basic bash prompts (fallback)
-
Output Format:
- Nickel config file (
.ncl) - JSON export for Nushell (
.json)
- Nickel config file (
-
File Locations:
- Forms:
provisioning/.typedialog/core/forms/ - Generated configs:
provisioning/.typedialog/core/generated/ - Templates:
provisioning/.typedialog/core/templates/
- Forms:
-
Error Handling:
- Exit code 0 = success
- Exit code 1 = failure/cancelled
- Stderr for error messages
TypeDialog Integration
These wrappers use TypeDialog forms when available:
# TypeDialog form location
FORM_PATH="provisioning/.typedialog/core/forms/setup-wizard.toml"
# Run TypeDialog
if command -v typedialog &> /dev/null; then
typedialog form "$FORM_PATH" \
--output "$OUTPUT_NCL" \
--backend "${TYPEDIALOG_BACKEND:-tui}"
# Export to JSON for Nushell
nickel export --format json "$OUTPUT_NCL" > "$OUTPUT_JSON"
fi
Fallback Behavior
If TypeDialog is not available, wrappers fall back to basic prompts:
# Fallback to basic bash prompts
echo "TypeDialog not available. Using basic prompts..."
read -p "Username: " username
read -sp "Password: " password
This ensures the system always works, even without TypeDialog installed.
When to Create a New Wrapper
Create a new TTY wrapper when:
- ✅ Interactive input is required (user must enter data)
- ✅ Called from Nushell context (execution stack issues)
- ✅ TTY file descriptors matter (TypeDialog, password prompts, etc.)
Do NOT create a wrapper when:
- ❌ Script is non-interactive (no user input)
- ❌ Script only processes files (no TTY needed)
- ❌ Script is already bash (no Nushell context)
Troubleshooting
Wrapper Not Found
# Check wrapper exists and is executable
ls -l provisioning/core/shlib/setup-wizard-tty.sh
# Make executable if needed
chmod +x provisioning/core/shlib/setup-wizard-tty.sh
TTY Input Still Fails
# Ensure running from proper TTY
tty # Should show /dev/ttys000 or similar
# Check stdin is connected to TTY
[ -t 0 ] && echo "stdin is TTY" || echo "stdin is NOT TTY"
# Run wrapper directly (bypass Nushell)
bash provisioning/core/shlib/setup-wizard-tty.sh
JSON Output Not Generated
# Check TypeDialog and Nickel are installed
command -v typedialog
command -v nickel
# Check output directory exists
mkdir -p provisioning/.typedialog/core/generated
# Check permissions
ls -ld provisioning/.typedialog/core/generated
Related Documentation
- TypeDialog Forms:
provisioning/.typedialog/core/forms/README.md - Nushell Integration:
provisioning/core/nulib/lib_provisioning/setup/wizard.nu - Architecture Decision:
docs/architecture/adr/ADR-XXX-tty-wrappers.md
Future Improvements
Potential enhancements (when needed):
- Caching: Store previous inputs for faster re-runs
- Validation: Pre-validate inputs before calling TypeDialog
- Multi-backend: Support web/tui/cli backends dynamically
- Batch mode: Support non-interactive mode with config file input
Version: 1.0.0 Last Updated: 2025-01-09 Status: Production Ready Maintainer: Provisioning Core Team