# Provisioning CLI - Flow-Aware TTY Command Management ## Architecture Overview The provisioning wrapper (`provisioning/core/cli/provisioning`) is a **flow controller** that manages three execution paths for command handling: 1. **Standalone TTY** - Interactive commands that exit after execution 2. **Pipeline TTY** - Interactive commands that output for piping to other commands 3. **Regular** - Standard Nushell command processing This design enables: - Interactive commands (TTY input) without blocking Nushell - Inter-command piping of TTY output to subsequent commands - Same-command flow (TTY input → Nushell processing in one execution) - Daemon optimization for non-interactive commands ## How Flow Management Works ### Execution Flow ```text User Command: provisioning ↓ Bash wrapper (provisioning) ↓ ┌──────────────────────────────────────┐ │ Phase 1: TTY Command Detection │ │ - Read tty-commands.conf registry │ │ - Match command pattern │ └──────────────────────────────────────┘ ↓ ├─→ Not a TTY command → Continue to Nushell (normal processing) │ └─→ TTY command found → Check flow type ↓ ├─→ flow=exit → Execute wrapper, exit immediately ├─→ flow=pipe → Execute wrapper, output to stdout, exit (allows piping) └─→ flow=continue → Execute wrapper, capture output, continue to Nushell ($env.TTY_OUTPUT available in Nushell) ``` ### Flow Types Explained #### 1. Standalone TTY Commands (flow=exit) **Use case**: Interactive forms, setup wizards, authentication dialogs **Example**: `provisioning setup wizard` **Flow**: ```bash Bash wrapper → TTY filter detects "setup wizard" → flow=exit ↓ Execute wrapper: core/shlib/setup-wizard-tty.sh ↓ User interaction (TypeDialog form) ↓ Exit wrapper → Exit bash wrapper ↓ Never reaches Nushell ``` **Registry entry**: ```bash "setup wizard" "core/shlib/setup-wizard-tty.sh" "exit" ``` #### 2. Pipeline TTY Commands (flow=pipe) **Use case**: Getting user input to pipe to another command **Example**: `provisioning auth get-key | provisioning deploy --api-key-stdin` **Flow**: ```bash Bash wrapper → TTY filter detects "auth get-key" → flow=pipe ↓ Execute wrapper: core/shlib/auth-get-key-tty.sh ↓ User provides API key via TTY prompt ↓ Wrapper outputs API key to stdout ↓ Exit wrapper (process exits, pipe has captured output) ↓ Next command receives API key from stdin ``` **Registry entry**: ```bash "auth get-key" "core/shlib/auth-get-key-tty.sh" "pipe" ``` **Wrapper requirements** (flow=pipe): - Must output result to stdout - Output must be newline-terminated - Exit with proper code (0=success, non-zero=error) #### 3. Continue-to-Nushell TTY Commands (flow=continue) **Use case**: TTY input that needs further processing in Nushell **Example**: `provisioning auth integrate --provider azure` **Flow**: ```bash Bash wrapper → TTY filter detects "auth integrate" → flow=continue ↓ Execute wrapper: core/shlib/auth-integrate-tty.sh ↓ User provides credentials via TTY prompt ↓ Wrapper outputs credentials (usually JSON) to stdout ↓ Filter CAPTURES output to $TTY_OUTPUT environment variable ↓ Set $env.PROVISIONING_BYPASS_DAEMON=true (skip daemon) ↓ Return 0 WITHOUT EXITING (continue to Nushell) ↓ Nushell dispatcher receives both: - CLI args: --provider azure - TTY output: $env.TTY_OUTPUT (credentials JSON) ↓ Nushell script processes both, completes integration ``` **Registry entry**: ```bash "auth integrate" "core/shlib/auth-integrate-tty.sh" "continue" ``` **Wrapper requirements** (flow=continue): - Must output result to stdout (usually JSON for structured data) - Exit with proper code (0=success, non-zero=error) **Nushell script requirements** (receives flow=continue output): ```nushell export def "provisioning auth integrate" [--provider: string] { # Check if TTY output exists (guard pattern) let tty_output = ($env.TTY_OUTPUT? | default "") if ($tty_output | is-empty) { error make {msg: "No credentials provided via TTY"} } # Parse TTY output (credentials) let credentials = ($tty_output | from json) # Use both TTY input ($credentials) and CLI args ($provider) # Complete integration logic... # Clear sensitive data after use hide-env TTY_OUTPUT } ``` #### 4. Regular Commands **Use case**: Standard provisioning operations **Example**: `provisioning server list` **Flow**: ```bash Bash wrapper → TTY filter checks registry → Not found → Return 1 ↓ Continue to normal processing: - Fast-path checks (help, workspace, env, etc.) - Daemon check (if applicable) - Nushell dispatcher ``` ## Registry Format **File**: `provisioning/core/cli/tty-commands.conf` **Three-field format**: `"PATTERN" "WRAPPER_PATH" "FLOW_TYPE"` ```bash # Exact command match (e.g., "setup wizard" matches "provisioning setup wizard") "setup wizard" "core/shlib/setup-wizard-tty.sh" "exit" # Paths are relative to $PROVISIONING "auth get-key" "core/shlib/auth-get-key-tty.sh" "pipe" # Flow types: exit | pipe | continue "auth integrate" "core/shlib/auth-integrate-tty.sh" "continue" ``` ### Flow Type Decision Matrix | Interaction | Flow Type | Example | | ----------- | --------- | ------- | | Interactive form, no output needed | `exit` | Setup wizard, auth login | | User input → pipe to next command | `pipe` | API key for piping to deploy | | User input → same-command Nushell processing | `continue` | Credentials for integration | ## Adding New TTY Commands ### Step 1: Create Wrapper Script Create wrapper in `provisioning/core/shlib/`: ```bash #!/bin/bash set -euo pipefail main() { local input # Get input from user read -rsp "Prompt: " input echo # Newline # For flow=pipe: output to stdout # For flow=continue: output to stdout (will be captured by filter) echo "$input" return 0 } main "$@" ``` Make it executable: ```bash chmod +x provisioning/core/shlib/your-wrapper-tty.sh ``` ### Step 2: Add Registry Entry Edit `provisioning/core/cli/tty-commands.conf`: ```bash # Standalone TTY "your command" "core/shlib/your-wrapper-tty.sh" "exit" # Pipeline TTY "get something" "core/shlib/get-something-tty.sh" "pipe" # Continue-to-Nushell TTY "setup something" "core/shlib/setup-something-tty.sh" "continue" ``` ### Step 3: No Wrapper Modifications Required The provisioning wrapper automatically: - Reads registry - Matches command pattern - Routes based on flow type - Handles all three flows **No need to modify provisioning wrapper for new commands!** ## Wrapper Script Requirements ### For All Wrappers - **Shebang**: `#!/bin/bash` - **Safety**: `set -euo pipefail` - **Arguments**: Accept `"${@}"` from wrapper - **Exit codes**: 0=success, non-zero=error - **Validation**: `shellcheck` passes without warnings ### For flow=exit Wrappers - Complete all interaction in wrapper - Exit with proper code (0=success, non-zero=error) - Output shown directly to user (from wrapper) ### For flow=pipe Wrappers - Get input from user (TTY) - Output result to stdout - Output must be newline-terminated - Exit with proper code (0=success, non-zero=error) ### For flow=continue Wrappers - Get input from user (TTY) - Output result to stdout (usually JSON) - Exit with proper code (0=success, non-zero=error) - Filter captures output → $TTY_OUTPUT - Nushell script reads $env.TTY_OUTPUT ## Environment Variables ### Exported by Filter (flow=continue only) - **`$TTY_OUTPUT`**: Captured output from wrapper (available in Nushell as `$env.TTY_OUTPUT`) - **`$PROVISIONING_BYPASS_DAEMON`**: Set to "true" to skip daemon (flow=continue automatically sets this) - **`$TTY_WRAPPER_EXECUTED`**: Set to "true" when TTY wrapper was executed ### Usage in Nushell ```nushell # Access TTY output in Nushell script export def "provisioning auth integrate" [--provider: string] { let tty_output = ($env.TTY_OUTPUT? | default "") # Parse if JSON let creds = ($tty_output | from json) # Use both TTY output and CLI args integration-logic $provider $creds # Clear after use (security) hide-env TTY_OUTPUT } ``` ## Daemon Interaction The flow filter intelligently manages daemon usage: ### For flow=exit and flow=pipe - ✅ **Daemon can be used** - No stdin required - No output needs to be captured and passed to Nushell - Daemon optimization available (~100ms startup improvement) ### For flow=continue - ❌ **Daemon MUST be bypassed** - stdin required for TTY interaction - `PROVISIONING_BYPASS_DAEMON=true` automatically set by filter - Direct Nushell execution (preserves stdin for TTY) - Zero overhead (same as non-daemon path) ## Testing TTY Commands ### Test Standalone (flow=exit) ```bash provisioning setup wizard # Expected: TypeDialog form, user interaction, exits ``` ### Test Pipeline (flow=pipe) ```bash provisioning auth get-key | wc -c # Expected: Prompts for API key, outputs to pipe ``` ### Test Continue (flow=continue) ```bash provisioning auth integrate --provider azure # Expected: Prompts for credentials, passes to Nushell with $env.TTY_OUTPUT ``` ### Test Regular Command ```bash provisioning server list # Expected: Normal Nushell processing ``` ## Troubleshooting ### Command Not Executed - **Check**: Is command in tty-commands.conf? - **Check**: Does pattern exactly match command? - **Check**: Is wrapper path correct and executable? ### Wrapper Not Found - **Error message**: `Warning: TTY wrapper not found or not executable: /path/to/wrapper` - **Check**: File exists at `$PROVISIONING/wrapper-path` - **Check**: File is executable: `chmod +x wrapper-path` ### Output Not Piping (flow=pipe) - **Check**: Wrapper outputs to stdout (not stderr) - **Check**: Output is newline-terminated: `echo "output"` - **Check**: No daemon interference (PROVISIONING_BYPASS_DAEMON not set) ### Nushell Not Receiving Output (flow=continue) - **Check**: `$env.TTY_OUTPUT` accessible in Nushell: `echo $env.TTY_OUTPUT` - **Check**: Output format (usually JSON): `echo $env.TTY_OUTPUT | from json` - **Check**: Wrapper exits with 0: `echo $?` ## Implementation Details ### Filter Location and Function **File**: `provisioning/core/cli/tty-filter.sh` **Function**: `filter_tty_command()` **Lines**: ~104 (includes documentation and three flow paths) ### Integration in Wrapper **File**: `provisioning/core/cli/provisioning` **Lines**: ~20 (sources filter, calls function, continues to Nushell) ### Registry Parsing - **File**: `provisioning/core/cli/tty-commands.conf` - **Method**: Line-by-line bash read (no jq dependency) - **Format**: Three-field bash array (bash-compatible) - **Sections**: Organized by flow type for clarity ## Performance Implications ### startup time - **flow=exit/pipe**: Daemon available for startup optimization (~100ms improvement) - **flow=continue**: Daemon bypassed (stdin needed), ~500ms traditional path - **Regular commands**: Normal daemon/non-daemon path selection ### Memory - **flow=continue**: Wrapper output stored in `$TTY_OUTPUT` environment variable - Typical size: < 1KB (credentials, keys, etc.) - Cleared after Nushell processing (or via `hide-env`) ## Security Considerations ### Sensitive Data in $TTY_OUTPUT - **Credentials** captured in `$TTY_OUTPUT` - **Nushell scripts should clear after use**: `hide-env TTY_OUTPUT` - **Wrapper output may be logged**: Use standard Unix conventions (hide passwords from output) ### Wrapper Location Restriction - Wrappers should be in `provisioning/core/shlib/` or `provisioning/scripts/` - Registry reads only wrappers from these trusted locations - Pattern validation prevents arbitrary script execution ### No Shell Injection - All variables quoted: `"$variable"` - No eval or command substitution with user input - Pattern matching uses exact string match (no regex) ## Related Files - **Filter**: `provisioning/core/cli/tty-filter.sh` - **Registry**: `provisioning/core/cli/tty-commands.conf` - **Wrapper**: `provisioning/core/cli/provisioning` - **Example wrappers**: `provisioning/core/shlib/auth-get-key-tty.sh`, `provisioning/core/shlib/auth-integrate-tty.sh` ## Key Insights The provisioning wrapper is not just a pass-through - it's a **flow controller** that: 1. **Detects TTY requirements** (registry matching) 2. **Manages execution paths** (three flows: exit, pipe, continue) 3. **Controls exit behavior** (standalone vs pipeline vs same-command) 4. **Enables inter-command piping** (TTY output to pipes) 5. **Supports Nushell integration** (TTY→Nushell continuation) 6. **Optimizes with daemon** (skip when stdin needed) This solves: - "el tema no es sólo un filter" → ✅ Flow controller with three execution paths - "cómo gestionar el flow por medio del provisioning command" → ✅ Registry + flow types - "usamos tty para input de una API key, se lo pasamos a un script de nushell" → ✅ Pipeline + continue flows --- **Version**: 1.0.0 **Last Updated**: January 2026 **Status**: ✅ Production Ready