prvng_core/cli
Jesús Pérez bea0477b25
refactor(18 files): selective imports — drive to 94.6% elimination (ADR-025 L2/L3)
Final mega-batch of single-star conversions combined in one commit.

=== Orchestrator facades (Layer 3, expanded to explicit symbol lists) ===
  config/accessor.nu            18 symbols (bridges accessor/mod)
  config/accessor_generated.nu  18 symbols (consumer of accessor)
  utils/version.nu              35 symbols (bridges version/mod)
  dependencies/mod.nu            7 symbols from resolver.nu
  oci_registry/mod.nu           12 multi-word "oci-registry X" subcommands
  oci/commands.nu               12 symbols from oci/client.nu
                                + removed redundant `use ./client.nu *` that
                                  was duplicated below the selective import

=== Selective imports (Layer 2) ===
  platform/discovery.nu         target.nu [5 symbols]
  platform/health.nu            target.nu [2 symbols]
  platform/connection.nu        user/config [get-active-workspace]
  vm/preparer.nu                vm/detector [check-vm-capability]
  vm/backend_libvirt.nu         result.nu [7 symbols]
  extensions/tests/test_versions.nu  versions [5 symbols]
  utils/version/loader.nu       nickel_processor [ncl-eval ncl-eval-soft]

=== Dead imports dropped ===
  platform/credentials.nu       user/config
  platform/activation.nu        target
  config/cache/core.nu          cache/metadata
  config/interpolation/core.nu  helpers/environment
  utils/version/loader.nu       version/core (kept nickel_processor)

=== Also included (pre-existing edits from earlier session) ===
  utils/settings.nu             pilot selective imports — reformatted
                                (file was modified externally during session)

Validation: all 18 files match pre-existing baselines (0 errors for clean
ones; 4/18/24/45/50/50 for pre-existing transitive noise).

MILESTONE: 94.6% of star-imports eliminated (370 → 20).

Remaining 20 star-lines in 5 files are intentional:
- lib_provisioning/mod.nu      (13 stars — root facade; empties in ADR-025 Phase 4)
- integrations/mod.nu          (2 stars — re-exports already-selective children)
- cmd/environment.nu           (3 stars — contains ~7 undefined function calls;
                                needs Blocker-1 style cleanup follow-up)
- providers/loader.nu          (1 dynamic `use ($entry_point) *` — runtime dispatch)
- vm/cleanup_scheduler.nu      (1 in string template — not a real import)

Refs: ADR-025
2026-04-17 17:10:47 +01:00
..
.gitkeep chore: codebase 2025-10-07 10:32:04 +01:00
cache feat: update provisioning core CLI, libraries, and plugins 2025-12-11 21:57:05 +00:00
cfssl-install.sh chore: update scripts 2026-01-08 21:14:49 +00:00
install_config.sh chore: codebase 2025-10-07 10:32:04 +01:00
install_nu.sh chore: update scripts 2026-01-08 21:14:49 +00:00
module-loader chore: update scripts 2026-01-08 21:14:49 +00:00
new_provisioning feat(core): three-layer DAG, unified component arch, commands-registry cache, Nushell 0.112.2 migration 2026-04-17 04:27:33 +01:00
old_provisioning feat(core): three-layer DAG, unified component arch, commands-registry cache, Nushell 0.112.2 migration 2026-04-17 04:27:33 +01:00
pack chore: update scripts 2026-01-08 21:14:49 +00:00
port-manager feat: update provisioning core CLI, libraries, and plugins 2025-12-11 21:57:05 +00:00
providers-install chore: update scripts 2026-01-08 21:14:49 +00:00
provisioning refactor(18 files): selective imports — drive to 94.6% elimination (ADR-025 L2/L3) 2026-04-17 17:10:47 +01:00
README.md feat(core): three-layer DAG, unified component arch, commands-registry cache, Nushell 0.112.2 migration 2026-04-17 04:27:33 +01:00
tools-install chore: update scripts 2026-01-08 21:14:49 +00:00
tty-commands.conf feat(core): three-layer DAG, unified component arch, commands-registry cache, Nushell 0.112.2 migration 2026-04-17 04:27:33 +01:00
tty-dispatch.sh feat(core): three-layer DAG, unified component arch, commands-registry cache, Nushell 0.112.2 migration 2026-04-17 04:27:33 +01:00
tty-filter.sh feat(core): three-layer DAG, unified component arch, commands-registry cache, Nushell 0.112.2 migration 2026-04-17 04:27:33 +01:00

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

User Command: provisioning <cmd> <args>
       ↓
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 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:

"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 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:

"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 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:

"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):

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 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"

# 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/:

#!/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:

chmod +x provisioning/core/shlib/your-wrapper-tty.sh

Step 2: Add Registry Entry

Edit provisioning/core/cli/tty-commands.conf:

# 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

# 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)

provisioning setup wizard
# Expected: TypeDialog form, user interaction, exits

Test Pipeline (flow=pipe)

provisioning auth get-key | wc -c
# Expected: Prompts for API key, outputs to pipe

Test Continue (flow=continue)

provisioning auth integrate --provider azure
# Expected: Prompts for credentials, passes to Nushell with $env.TTY_OUTPUT

Test Regular Command

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)
  • 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