prvng_core/cli/README.md

468 lines
13 KiB
Markdown
Raw Normal View History

feat(core): three-layer DAG, unified component arch, commands-registry cache, Nushell 0.112.2 migration - DAG architecture: `dag show/validate/export` (nulib/main_provisioning/dag.nu), config loader (lib_provisioning/config/loader/dag.nu), taskserv dag-executor. Backed by schemas/lib/dag/*.ncl; orchestrator emits NATS events via WorkspaceComposition::into_workflow. See ADR-020, ADR-021. - Unified Component Architecture: components/mod.nu, main_provisioning/ {components,workflow,extensions,ontoref-queries}.nu. Full workflow engine with topological sort and NATS subject emission. Blocks A-H complete (libre-daoshi). - Commands-registry: nulib/commands-registry.ncl (Nickel source, 314 lines) + JSON cache at ~/.cache/provisioning/commands-registry.json rebuilt on source change. cli/provisioning fast-path alias expansion avoids cold Nu startup. ADDING_COMMANDS.md documents new-command workflow. - Platform service manager: service-manager.nu (+573), startup.nu (+611), service-check.nu (+255); autostart/bootstrap/health/target refactored. - Nushell 0.112.2 migration: removed all try/catch and bash redirections; external commands prefixed with ^; type signatures enforced. Driven by scripts/refactor-try-catch{,-simplified}.nu. - TTY stack: removed shlib/*-tty.sh; replaced by cli/tty-dispatch.sh, tty-filter.sh, tty-commands.conf. - New domain modules: images/ (golden image lifecycle), workspace/{state,sync}.nu, main_provisioning/{bootstrap,cluster-deploy,fip,state}.nu, commands/{state, build,integrations/auth,utilities/alias}.nu, platform.nu expanded (+874). - Config loader overhaul: loader/core.nu slimmed (-759), cache/core.nu refactored (-454), removed legacy loaders/file_loader.nu (-330). - Thirteen new provisioning-<domain>.nu top-level modules for bash dispatcher. - Tests: test_workspace_state.nu (+351); updates to test_oci_registry, test_services. - README + CHANGELOG updated.
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
```text
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
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