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 input
  • auth-login-tty.sh - Authentication login with TTY-safe input
  • mfa-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:

  1. Input Modes (fallback chain):

    • TypeDialog interactive forms (if binary available)
    • Basic bash prompts (fallback)
  2. Output Format:

    • Nickel config file (.ncl)
    • JSON export for Nushell (.json)
  3. File Locations:

    • Forms: provisioning/.typedialog/core/forms/
    • Generated configs: provisioning/.typedialog/core/generated/
    • Templates: provisioning/.typedialog/core/templates/
  4. 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:

  1. Interactive input is required (user must enter data)
  2. Called from Nushell context (execution stack issues)
  3. 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
  • 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):

  1. Caching: Store previous inputs for faster re-runs
  2. Validation: Pre-validate inputs before calling TypeDialog
  3. Multi-backend: Support web/tui/cli backends dynamically
  4. 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