236 lines
8.3 KiB
Markdown
236 lines
8.3 KiB
Markdown
# 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:
|
|
|
|
```nushell
|
|
# 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:
|
|
|
|
```text
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ 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
|
|
|
|
```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
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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):
|
|
|
|
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
|