# Utility Command Handlers # Handles: ssh, sed, sops, cache, providers, nu, list, qr use ../flags.nu * use ../../lib_provisioning * use ../../servers/ssh.nu * use ../../servers/utils.nu * # Helper to run module commands def run_module [ args: string module: string option?: string --exec ] { let use_debug = if ($env.PROVISIONING_DEBUG? | default false) { "-x" } else { "" } if $exec { exec $"($env.PROVISIONING_NAME)" $use_debug -mod $module ($option | default "") $args } else { ^$"($env.PROVISIONING_NAME)" $use_debug -mod $module ($option | default "") $args } } # Main utility command dispatcher export def handle_utility_command [ command: string ops: string flags: record ] { match $command { "ssh" => { handle_ssh $flags } "sed" | "sops" => { handle_sops_edit $command $ops $flags } "cache" => { handle_cache $ops $flags } "providers" => { handle_providers $ops $flags } "nu" => { handle_nu $ops $flags } "list" | "l" | "ls" => { handle_list $ops $flags } "qr" => { handle_qr } "nuinfo" => { handle_nuinfo } "plugin" | "plugins" => { handle_plugins $ops $flags } "guide" | "guides" | "howto" => { handle_guide $ops $flags } _ => { print $"❌ Unknown utility command: ($command)" print "" print "Available utility commands:" print " ssh - SSH into server" print " sed - Edit SOPS encrypted files (alias)" print " sops - Edit SOPS encrypted files" print " cache - Cache management (status, config, clear, list)" print " providers - List available providers" print " nu - Start Nushell with provisioning library loaded" print " list - List resources (servers, taskservs, clusters)" print " qr - Generate QR code" print " nuinfo - Show Nushell version info" print " plugin - Plugin management (list, register, test, status)" print " guide - Show interactive guides (from-scratch, update, customize)" print "" print "Use 'provisioning help utilities' for more details" exit 1 } } } # SSH command handler def handle_ssh [flags: record] { let curr_settings = (find_get_settings --infra $flags.infra --settings $flags.settings $flags.include_notuse) rm -rf $curr_settings.wk_path server_ssh $curr_settings "" "pub" false } # SOPS edit command handler def handle_sops_edit [task: string, ops: string, flags: record] { let pos = if $task == "sed" { 0 } else { 1 } let ops_parts = ($ops | split row " ") let target_file = if ($ops_parts | length) > $pos { $ops_parts | get $pos } else { "" } if ($target_file | is-empty) { throw-error $"🛑 No file found" $"for (_ansi yellow_bold)sops(_ansi reset) edit" exit -1 } let target_full_path = if not ($target_file | path exists) { let infra_path = (get_infra $flags.infra) let candidate = ($infra_path | path join $target_file) if ($candidate | path exists) { $candidate } else { throw-error $"🛑 No file (_ansi green_italic)($target_file)(_ansi reset) found" $"for (_ansi yellow_bold)sops(_ansi reset) edit" exit -1 } } else { $target_file } # Setup SOPS environment if needed if ($env.PROVISIONING_SOPS? | is-empty) { let curr_settings = (find_get_settings --infra $flags.infra --settings $flags.settings $flags.include_notuse) rm -rf $curr_settings.wk_path $env.CURRENT_INFRA_PATH = ($curr_settings.infra_path | path join $curr_settings.infra) use ../../sops_env.nu } if $task == "sed" { on_sops "sed" $target_full_path } else { on_sops $task $target_full_path ($ops_parts | skip 1) } } # Cache command handler def handle_cache [ops: string, flags: record] { use ../../lib_provisioning/config/cache/simple-cache.nu * # Parse cache subcommand let parts = if ($ops | is-not-empty) { ($ops | str trim | split row " " | where { |x| ($x | is-not-empty) }) } else { [] } let subcommand = if ($parts | length) > 0 { $parts | get 0 } else { "status" } let args = if ($parts | length) > 1 { $parts | skip 1 } else { [] } # Handle cache commands match $subcommand { "status" => { print "" cache-status print "" } "config" => { let config_cmd = if ($args | length) > 0 { $args | get 0 } else { "show" } match $config_cmd { "show" => { print "" let config = (get-cache-config) let cache_base = (($env.HOME? | default "~" | path expand) | path join ".provisioning" "cache" "config") print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" print "📋 Cache Configuration" print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" print "" print "▸ Core Settings:" let enabled = ($config | get --optional enabled | default true) print (" Enabled: " + ($enabled | into string)) print "" print "▸ Cache Location:" print (" Base Path: " + $cache_base) print "" print "▸ Time-To-Live (TTL) Settings:" let ttl_final = ($config | get --optional ttl_final_config | default "300") let ttl_kcl = ($config | get --optional ttl_kcl | default "1800") let ttl_sops = ($config | get --optional ttl_sops | default "900") print (" Final Config: " + ($ttl_final | into string) + "s (5 minutes)") print (" KCL Compilation: " + ($ttl_kcl | into string) + "s (30 minutes)") print (" SOPS Decryption: " + ($ttl_sops | into string) + "s (15 minutes)") print " Provider Config: 600s (10 minutes)" print " Platform Config: 600s (10 minutes)" print "" print "▸ Security Settings:" print " SOPS File Permissions: 0600 (owner read-only)" print " SOPS Directory Permissions: 0700 (owner access only)" print "" print "▸ Validation Settings:" print " Strict mtime Checking: true (validates all source files)" print "" print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" print "" } "get" => { if ($args | length) > 1 { let setting = $args | get 1 let value = (cache-config-get $setting) if $value != null { print $"($setting) = ($value)" } else { print $"Setting not found: ($setting)" } } else { print "❌ cache config get requires a setting path" print "Usage: provisioning cache config get " exit 1 } } "set" => { if ($args | length) > 2 { let setting = $args | get 1 let value = ($args | skip 2 | str join " ") cache-config-set $setting $value print $"✓ Set ($setting) = ($value)" } else { print "❌ cache config set requires setting path and value" print "Usage: provisioning cache config set " exit 1 } } _ => { print $"❌ Unknown cache config subcommand: ($config_cmd)" print "" print "Available cache config subcommands:" print " show - Show all cache configuration" print " get - Get specific cache setting" print " set - Set cache setting" print "" print "Available settings for get/set:" print " enabled - Cache enabled (true/false)" print " ttl_final_config - TTL for final config (seconds)" print " ttl_kcl - TTL for KCL compilation (seconds)" print " ttl_sops - TTL for SOPS decryption (seconds)" print "" print "Examples:" print " provisioning cache config show" print " provisioning cache config get ttl_final_config" print " provisioning cache config set ttl_final_config 600" exit 1 } } } "clear" => { let cache_type = if ($args | length) > 0 { $args | get 0 } else { "all" } cache-clear $cache_type print $"✓ Cleared cache: ($cache_type)" } "list" => { let cache_type = if ($args | length) > 0 { $args | get 0 } else { "*" } let items = (cache-list $cache_type) if ($items | length) > 0 { print $"Cache items \(type: ($cache_type)\):" $items | each { |item| print $" ($item)" } } else { print "No cache items found" } } "help" => { print " Cache Management Commands: provisioning cache status # Show cache status and statistics provisioning cache config show # Show cache configuration provisioning cache config get # Get specific cache setting provisioning cache config set # Set cache setting provisioning cache clear [type] # Clear cache (default: all) provisioning cache list [type] # List cached items (default: all) provisioning cache help # Show this help message Available settings (for get/set): enabled - Cache enabled (true/false) ttl_final_config - TTL for final config (seconds) ttl_kcl - TTL for KCL compilation (seconds) ttl_sops - TTL for SOPS decryption (seconds) Examples: provisioning cache status provisioning cache config get ttl_final_config provisioning cache config set ttl_final_config 600 provisioning cache config set enabled false provisioning cache clear kcl provisioning cache list " } _ => { print $"❌ Unknown cache command: ($subcommand)" print "" print "Available cache commands:" print " status - Show cache status and statistics" print " config show - Show cache configuration" print " config get - Get specific cache setting" print " config set - Set cache setting" print " clear [type] - Clear cache (all, kcl, sops, final)" print " list [type] - List cached items" print " help - Show this help message" print "" print "Examples:" print " provisioning cache status" print " provisioning cache config get ttl_final_config" print " provisioning cache config set ttl_final_config 600" print " provisioning cache clear kcl" exit 1 } } } # Providers command handler - supports list, info, install, remove, installed, validate def handle_providers [ops: string, flags: record] { use ../../lib_provisioning/kcl_module_loader.nu * # Parse subcommand and arguments let parts = if ($ops | is-not-empty) { ($ops | str trim | split row " " | where { |x| ($x | is-not-empty) }) } else { [] } let subcommand = if ($parts | length) > 0 { $parts | get 0 } else { "list" } let args = if ($parts | length) > 1 { $parts | skip 1 } else { [] } match $subcommand { "list" => { handle_providers_list $flags $args } "info" => { handle_providers_info $args $flags } "install" => { handle_providers_install $args $flags } "remove" => { handle_providers_remove $args $flags } "installed" => { handle_providers_installed $args $flags } "validate" => { handle_providers_validate $args $flags } "help" | "-h" | "--help" => { show_providers_help } _ => { print $"❌ Unknown providers subcommand: ($subcommand)" print "" show_providers_help exit 1 } } } # List all available providers def handle_providers_list [flags: record, args: list] { use ../../lib_provisioning/kcl_module_loader.nu * _print $"(_ansi green)PROVIDERS(_ansi reset) list: \n" # Parse flags let show_kcl = ($args | any { |x| $x == "--kcl" }) let format_idx = ($args | enumerate | where item == "--format" | get 0?.index | default (-1)) let format = if $format_idx >= 0 and ($args | length) > ($format_idx + 1) { $args | get ($format_idx + 1) } else { "table" } let no_cache = ($args | any { |x| $x == "--no-cache" }) # Get providers using cached KCL module loader let providers = if $no_cache { (discover-kcl-modules "providers") } else { (discover-kcl-modules-cached "providers") } match $format { "json" => { _print ($providers | to json) "json" "result" "table" } "yaml" => { _print ($providers | to yaml) "yaml" "result" "table" } _ => { # Table format - show summary or full with --kcl if $show_kcl { _print ($providers | to json) "json" "result" "table" } else { # Show simplified table let simplified = ($providers | each {|p| {name: $p.name, type: $p.type, version: $p.version} }) _print ($simplified | to json) "json" "result" "table" } } } } # Show detailed provider information def handle_providers_info [args: list, flags: record] { use ../../lib_provisioning/kcl_module_loader.nu * if ($args | is-empty) { print "❌ Provider name required" print "Usage: provisioning providers info [--kcl] [--no-cache]" exit 1 } let provider_name = $args | get 0 let show_kcl = ($args | any { |x| $x == "--kcl" }) let no_cache = ($args | any { |x| $x == "--no-cache" }) print $"(_ansi blue_bold)📋 Provider Information: ($provider_name)(_ansi reset)" print "" let providers = if $no_cache { (discover-kcl-modules "providers") } else { (discover-kcl-modules-cached "providers") } let provider_info = ($providers | where name == $provider_name) if ($provider_info | is-empty) { print $"❌ Provider not found: ($provider_name)" exit 1 } let info = ($provider_info | first) print $" Name: ($info.name)" print $" Type: ($info.type)" print $" Path: ($info.path)" print $" Has KCL: ($info.has_kcl)" if $show_kcl and $info.has_kcl { print "" print " (_ansi cyan_bold)KCL Module:(_ansi reset)" print $" Module Name: ($info.kcl_module_name)" print $" KCL Path: ($info.kcl_path)" print $" Version: ($info.version)" print $" Edition: ($info.edition)" # Check for kcl.mod file let kcl_mod = ($info.kcl_path | path join "kcl.mod") if ($kcl_mod | path exists) { print "" print $" (_ansi cyan_bold)kcl.mod content:(_ansi reset)" open $kcl_mod | lines | each {|line| print $" ($line)"} } } print "" } # Install provider for infrastructure def handle_providers_install [args: list, flags: record] { use ../../lib_provisioning/kcl_module_loader.nu * if ($args | length) < 2 { print "❌ Provider name and infrastructure required" print "Usage: provisioning providers install [--version ]" exit 1 } let provider_name = $args | get 0 let infra_name = $args | get 1 # Extract version flag if present let version_idx = ($args | enumerate | where item == "--version" | get 0?.index | default (-1)) let version = if $version_idx >= 0 and ($args | length) > ($version_idx + 1) { $args | get ($version_idx + 1) } else { "0.0.1" } # Resolve infrastructure path let infra_path = (resolve_infra_path $infra_name) if ($infra_path | is-empty) { print $"❌ Infrastructure not found: ($infra_name)" exit 1 } # Install provider install-provider $provider_name $infra_path --version $version print "" print $"(_ansi yellow_bold)💡 Next steps:(_ansi reset)" print $" 1. Check the manifest: ($infra_path)/providers.manifest.yaml" print $" 2. Update server definitions to use ($provider_name)" print $" 3. Run: kcl run defs/servers.k" } # Remove provider from infrastructure def handle_providers_remove [args: list, flags: record] { use ../../lib_provisioning/kcl_module_loader.nu * if ($args | length) < 2 { print "❌ Provider name and infrastructure required" print "Usage: provisioning providers remove [--force]" exit 1 } let provider_name = $args | get 0 let infra_name = $args | get 1 let force = ($args | any { |x| $x == "--force" }) # Resolve infrastructure path let infra_path = (resolve_infra_path $infra_name) if ($infra_path | is-empty) { print $"❌ Infrastructure not found: ($infra_name)" exit 1 } # Confirmation unless forced if not $force { print $"(_ansi yellow)⚠️ This will remove provider ($provider_name) from ($infra_name)(_ansi reset)" print " KCL dependencies will be updated." let response = (input "Continue? (y/N): ") if ($response | str downcase) != "y" { print "❌ Cancelled" return } } # Remove provider remove-provider $provider_name $infra_path } # List installed providers for infrastructure def handle_providers_installed [args: list, flags: record] { if ($args | is-empty) { print "❌ Infrastructure name required" print "Usage: provisioning providers installed [--format ]" exit 1 } let infra_name = $args | get 0 # Parse format flag let format_idx = ($args | enumerate | where item == "--format" | get 0?.index | default (-1)) let format = if $format_idx >= 0 and ($args | length) > ($format_idx + 1) { $args | get ($format_idx + 1) } else { "table" } # Resolve infrastructure path let infra_path = (resolve_infra_path $infra_name) if ($infra_path | is-empty) { print $"❌ Infrastructure not found: ($infra_name)" exit 1 } let manifest_path = ($infra_path | path join "providers.manifest.yaml") if not ($manifest_path | path exists) { print $"❌ No providers.manifest.yaml found in ($infra_name)" exit 1 } let manifest = (open $manifest_path) let providers = if ($manifest | get providers? | is-not-empty) { $manifest | get providers } else if ($manifest | get loaded_providers? | is-not-empty) { $manifest | get loaded_providers } else { [] } print $"(_ansi blue_bold)📦 Installed providers for ($infra_name):(_ansi reset)" print "" match $format { "json" => { _print ($providers | to json) "json" "result" "table" } "yaml" => { _print ($providers | to yaml) "yaml" "result" "table" } _ => { _print ($providers | to json) "json" "result" "table" } } } # Validate provider installation def handle_providers_validate [args: list, flags: record] { use ../../lib_provisioning/kcl_module_loader.nu * if ($args | is-empty) { print "❌ Infrastructure name required" print "Usage: provisioning providers validate [--no-cache]" exit 1 } let infra_name = $args | get 0 let no_cache = ($args | any { |x| $x == "--no-cache" }) print $"(_ansi blue_bold)🔍 Validating providers for ($infra_name)...(_ansi reset)" print "" # Resolve infrastructure path let infra_path = (resolve_infra_path $infra_name) if ($infra_path | is-empty) { print $"❌ Infrastructure not found: ($infra_name)" exit 1 } mut validation_errors = [] # Check manifest exists let manifest_path = ($infra_path | path join "providers.manifest.yaml") if not ($manifest_path | path exists) { $validation_errors = ($validation_errors | append "providers.manifest.yaml not found") } else { # Check each provider in manifest let manifest = (open $manifest_path) let providers = ($manifest | get providers? | default []) # Load providers once using cache let all_providers = if $no_cache { (discover-kcl-modules "providers") } else { (discover-kcl-modules-cached "providers") } for provider in $providers { print $" Checking ($provider.name)..." # Check if provider exists in cached list let available = ($all_providers | where name == $provider.name) if ($available | is-empty) { $validation_errors = ($validation_errors | append $"Provider not found: ($provider.name)") print $" ❌ Not found in extensions" } else { let provider_info = ($available | first) # Check if symlink exists let modules_dir = ($infra_path | path join ".kcl-modules") let link_path = ($modules_dir | path join $provider_info.kcl_module_name) if not ($link_path | path exists) { $validation_errors = ($validation_errors | append $"Symlink missing: ($link_path)") print $" ❌ Symlink not found" } else { print $" ✓ OK" } } } } # Check kcl.mod let kcl_mod_path = ($infra_path | path join "kcl.mod") if not ($kcl_mod_path | path exists) { $validation_errors = ($validation_errors | append "kcl.mod not found") } print "" # Report results if ($validation_errors | is-empty) { print "(_ansi green)✅ Validation passed - all providers correctly installed(_ansi reset)" } else { print "(_ansi red)❌ Validation failed:(_ansi reset)" for error in $validation_errors { print $" • ($error)" } exit 1 } } # Helper: Resolve infrastructure path def resolve_infra_path [infra: string]: nothing -> string { if ($infra | path exists) { return $infra } # Try workspace/infra path let workspace_path = $"workspace/infra/($infra)" if ($workspace_path | path exists) { return $workspace_path } # Try absolute workspace path let proj_root = ($env.PROVISIONING_ROOT? | default "/Users/Akasha/project-provisioning") let abs_workspace_path = ($proj_root | path join "workspace" "infra" $infra) if ($abs_workspace_path | path exists) { return $abs_workspace_path } return "" } # Show providers help def show_providers_help [] { print $" (_ansi cyan_bold)╔══════════════════════════════════════════════════╗(_ansi reset) (_ansi cyan_bold)║(_ansi reset) 📦 PROVIDER MANAGEMENT (_ansi cyan_bold)║(_ansi reset) (_ansi cyan_bold)╚══════════════════════════════════════════════════╝(_ansi reset) (_ansi green_bold)[Available Providers](_ansi reset) (_ansi blue)provisioning providers list [--kcl] [--format ](_ansi reset) List all available providers Formats: table (default value), json, yaml (_ansi blue)provisioning providers info [--kcl](_ansi reset) Show detailed provider information with optional KCL details (_ansi green_bold)[Provider Installation](_ansi reset) (_ansi blue)provisioning providers install [--version ](_ansi reset) Install provider for an infrastructure Default version: 0.0.1 (_ansi blue)provisioning providers remove [--force](_ansi reset) Remove provider from infrastructure --force skips confirmation prompt (_ansi blue)provisioning providers installed [--format ](_ansi reset) List installed providers for infrastructure Formats: table (default value), json, yaml (_ansi blue)provisioning providers validate (_ansi reset) Validate provider installation and configuration (_ansi green_bold)EXAMPLES(_ansi reset) # List all providers provisioning providers list # Show KCL module details provisioning providers info upcloud --kcl # Install provider provisioning providers install upcloud myinfra # List installed providers provisioning providers installed myinfra # Validate installation provisioning providers validate myinfra # Remove provider provisioning providers remove aws myinfra --force (_ansi default_dimmed)💡 Use 'provisioning help providers' for more information(_ansi reset) " } # Nu shell command handler def handle_nu [ops: string, flags: record] { let run_ops = if ($ops | str trim | str starts-with "-") { "" } else { let parts = ($ops | split row " ") if ($parts | is-empty) { "" } else { $parts | first } } if ($flags.infra | is-not-empty) and ($env.PROVISIONING_INFRA_PATH | path join $flags.infra | path exists) { cd ($env.PROVISIONING_INFRA_PATH | path join $flags.infra) } if ($flags.output_format | is-empty) { if ($run_ops | is-empty) { print ( $"\nTo exit (_ansi purple_bold)NuShell(_ansi reset) session, with (_ansi default_dimmed)lib_provisioning(_ansi reset) loaded, " + $"use (_ansi green_bold)exit(_ansi reset) or (_ansi green_bold)[CTRL-D](_ansi reset)" ) # Pass the provisioning configuration files to the Nu subprocess # This ensures the interactive session has the same config loaded as the calling environment let config_path = ($env.PROVISIONING_CONFIG? | default "") # Build library paths argument - needed for module resolution during parsing # Convert colon-separated string to -I flag arguments let lib_dirs = ($env.NU_LIB_DIRS? | default "") let lib_paths = if ($lib_dirs | is-not-empty) { ($lib_dirs | split row ":" | where { |x| ($x | is-not-empty) }) } else { [] } if ($config_path | is-not-empty) { # Pass config files AND library paths via -I flags for module resolution # Library paths are set via -I flags which enables module resolution during parsing phase if ($lib_paths | length) > 0 { # Construct command with -I flags for each library path let cmd = (mut cmd_parts = []; for path in $lib_paths { $cmd_parts = ($cmd_parts | append "-I" | append $path) }; $cmd_parts) # Start interactive Nushell with provisioning configuration loaded # The -i flag enables interactive mode (REPL) with full terminal features ^nu --config $"($config_path)/config.nu" --env-config $"($config_path)/env.nu" ...$cmd -i } else { ^nu --config $"($config_path)/config.nu" --env-config $"($config_path)/env.nu" -i } } else { # Fallback if PROVISIONING_CONFIG not set if ($lib_paths | length) > 0 { let cmd = (mut cmd_parts = []; for path in $lib_paths { $cmd_parts = ($cmd_parts | append "-I" | append $path) }; $cmd_parts) ^nu ...$cmd -i } else { ^nu -i } } } else { # Also pass library paths for single command execution let lib_dirs = ($env.NU_LIB_DIRS? | default "") let lib_paths = if ($lib_dirs | is-not-empty) { ($lib_dirs | split row ":" | where { |x| ($x | is-not-empty) }) } else { [] } if ($lib_paths | length) > 0 { let cmd = (mut cmd_parts = []; for path in $lib_paths { $cmd_parts = ($cmd_parts | append "-I" | append $path) }; $cmd_parts) ^nu ...$cmd -c $"($run_ops)" } else { ^nu -c $"($run_ops)" } } } } # List command handler def handle_list [ops: string, flags: record] { let target_list = if ($ops | is-not-empty) { let parts = ($ops | split row " ") if ($parts | is-empty) { "" } else { $parts | first } } else { "" } let list_ops = ($ops | str replace $"($target_list) " "" | str trim) on_list $target_list ($flags.onsel | default "") $list_ops } # QR code command handler def handle_qr [] { make_qr } # Nu info command handler def handle_nuinfo [] { print $"\n (_ansi yellow)Nu shell info(_ansi reset)" print (version) } # Plugins command handler def handle_plugins [ops: string, flags: record] { let subcommand = if ($ops | is-not-empty) { ($ops | split row " " | get 0) } else { "list" } let remaining_ops = if ($ops | is-not-empty) { ($ops | split row " " | skip 1 | str join " ") } else { "" } match $subcommand { "list" | "ls" => { handle_plugin_list $flags } "register" | "add" => { handle_plugin_register $remaining_ops $flags } "test" => { handle_plugin_test $remaining_ops $flags } "build" => { handle_plugin_build $remaining_ops $flags } "status" => { handle_plugin_status $flags } "help" => { show_plugin_help } _ => { print $"❌ Unknown plugin subcommand: ($subcommand)" print "Use 'provisioning plugin help' for available commands" exit 1 } } } # List installed plugins with status def handle_plugin_list [flags: record] { use ../../lib_provisioning/plugins/mod.nu [list-plugins] print $"\n (_ansi cyan_bold)Installed Plugins(_ansi reset)\n" let plugins = (list-plugins) if ($plugins | length) > 0 { print ($plugins | table -e) } else { print "(_ansi yellow)No plugins found(_ansi reset)" } print $"\n(_ansi default_dimmed)💡 Use 'provisioning plugin register ' to register a plugin(_ansi reset)" } # Register plugin with Nushell def handle_plugin_register [ops: string, flags: record] { use ../../lib_provisioning/plugins/mod.nu [register-plugin] let plugin_name = if ($ops | is-not-empty) { ($ops | split row " " | get 0) } else { print $"(_ansi red)❌ Plugin name required(_ansi reset)" print $"Usage: provisioning plugin register " exit 1 } register-plugin $plugin_name } # Test plugin functionality def handle_plugin_test [ops: string, flags: record] { use ../../lib_provisioning/plugins/mod.nu [test-plugin] let plugin_name = if ($ops | is-not-empty) { ($ops | split row " " | get 0) } else { print $"(_ansi red)❌ Plugin name required(_ansi reset)" print $"Usage: provisioning plugin test " print $"Valid plugins: auth, kms, tera, kcl" exit 1 } test-plugin $plugin_name } # Build plugins from source def handle_plugin_build [ops: string, flags: record] { use ../../lib_provisioning/plugins/mod.nu [build-plugins] let plugin_name = if ($ops | is-not-empty) { ($ops | split row " " | get 0) } else { "" } if ($plugin_name | is-empty) { print $"\n(_ansi cyan)Building all plugins...(_ansi reset)" build-plugins } else { print $"\n(_ansi cyan)Building plugin: ($plugin_name)(_ansi reset)" build-plugins --plugin $plugin_name } } # Show plugin status def handle_plugin_status [flags: record] { use ../../lib_provisioning/plugins/mod.nu [plugin-build-info] use ../../lib_provisioning/plugins/auth.nu [plugin-auth-status] use ../../lib_provisioning/plugins/kms.nu [plugin-kms-info] print $"\n(_ansi cyan_bold)Plugin Status(_ansi reset)\n" print $"(_ansi yellow_bold)Authentication Plugin:(_ansi reset)" let auth_status = (plugin-auth-status) print $" Available: ($auth_status.plugin_available)" print $" Enabled: ($auth_status.plugin_enabled)" print $" Mode: ($auth_status.mode)" print $"\n(_ansi yellow_bold)KMS Plugin:(_ansi reset)" let kms_info = (plugin-kms-info) print $" Available: ($kms_info.plugin_available)" print $" Enabled: ($kms_info.plugin_enabled)" print $" Backend: ($kms_info.default_backend)" print $" Mode: ($kms_info.mode)" print $"\n(_ansi yellow_bold)Build Information:(_ansi reset)" let build_info = (plugin-build-info) if $build_info.exists { print $" Source directory: ($build_info.plugins_dir)" print $" Available sources: ($build_info.available_sources | length)" } else { print $" Source directory: Not found" } } # Show plugin help def show_plugin_help [] { print $" (_ansi cyan_bold)╔══════════════════════════════════════════════════╗(_ansi reset) (_ansi cyan_bold)║(_ansi reset) 🔌 PLUGIN MANAGEMENT (_ansi cyan_bold)║(_ansi reset) (_ansi cyan_bold)╚══════════════════════════════════════════════════╝(_ansi reset) (_ansi green_bold)[Plugin Operations](_ansi reset) (_ansi blue)plugin list(_ansi reset) List all plugins with status (_ansi blue)plugin register (_ansi reset) Register plugin with Nushell (_ansi blue)plugin test (_ansi reset) Test plugin functionality (_ansi blue)plugin build [name](_ansi reset) Build plugins from source (_ansi blue)plugin status(_ansi reset) Show plugin status and info (_ansi green_bold)[Available Plugins](_ansi reset) • (_ansi cyan)auth(_ansi reset) - JWT authentication with MFA support • (_ansi cyan)kms(_ansi reset) - Key Management Service integration • (_ansi cyan)tera(_ansi reset) - Template rendering engine • (_ansi cyan)kcl(_ansi reset) - KCL configuration language (_ansi green_bold)EXAMPLES(_ansi reset) # List all plugins provisioning plugin list # Register auth plugin provisioning plugin register nu_plugin_auth # Test KMS plugin provisioning plugin test kms # Build all plugins provisioning plugin build # Build specific plugin provisioning plugin build nu_plugin_auth # Show plugin status provisioning plugin status (_ansi default_dimmed)💡 Plugins provide HTTP fallback when not registered Authentication and KMS work in both plugin and HTTP modes(_ansi reset) " } # Guide command handler def handle_guide [ops: string, flags: record] { let guide_topic = if ($ops | is-not-empty) { ($ops | split row " " | get 0) } else { "" } # Define guide topics and their paths let guides = { "quickstart": "docs/guides/quickstart-cheatsheet.md", "from-scratch": "docs/guides/from-scratch.md", "scratch": "docs/guides/from-scratch.md", "start": "docs/guides/from-scratch.md", "deploy": "docs/guides/from-scratch.md", "list": "list_guides" } # Get docs directory let docs_dir = ($env.PROVISIONING_PATH | path join "docs" "guides") match $guide_topic { "" => { # Show guide list show_guide_list $docs_dir } "list" => { show_guide_list $docs_dir } _ => { # Try to find and display guide let guide_path = if ($guide_topic in ($guides | columns)) { $guides | get $guide_topic } else { null } if ($guide_path == null or $guide_path == "list_guides") { print $"(_ansi red)❌ Unknown guide:(_ansi reset) ($guide_topic)" print "" show_guide_list $docs_dir exit 1 } let full_path = ($env.PROVISIONING_PATH | path join $guide_path) if not ($full_path | path exists) { print $"(_ansi red)❌ Guide file not found:(_ansi reset) ($full_path)" exit 1 } # Display guide using best available viewer display_guide $full_path $guide_topic } } } # Display guide using best available markdown viewer def display_guide [ guide_path: path topic: string ] { print $"\n(_ansi cyan_bold)📖 Guide:(_ansi reset) ($topic)\n" # Check for viewers in order of preference: glow, bat, less, cat if (which glow | length) > 0 { ^glow $guide_path } else if (which bat | length) > 0 { ^bat --style=plain --paging=always $guide_path } else if (which less | length) > 0 { ^less $guide_path } else { open $guide_path } } # Show list of available guides def show_guide_list [docs_dir: path] { print $" (_ansi magenta_bold)╔══════════════════════════════════════════════════╗(_ansi reset) (_ansi magenta_bold)║(_ansi reset) 📚 AVAILABLE GUIDES (_ansi magenta_bold)║(_ansi reset) (_ansi magenta_bold)╚══════════════════════════════════════════════════╝(_ansi reset) (_ansi green_bold)[Step-by-Step Guides](_ansi reset) (_ansi blue)provisioning guide from-scratch(_ansi reset) Complete deployment from zero to production (_ansi default_dimmed)Shortcuts: scratch, start, deploy(_ansi reset) (_ansi green_bold)[Quick References](_ansi reset) (_ansi blue)provisioning guide quickstart(_ansi reset) Command shortcuts and quick reference (_ansi default_dimmed)Shortcuts: shortcuts, quick(_ansi reset) (_ansi green_bold)USAGE(_ansi reset) # View guide provisioning guide # List all guides provisioning guide list provisioning howto (_ansi default_dimmed)# shortcut(_ansi reset) (_ansi green_bold)EXAMPLES(_ansi reset) # Complete deployment guide provisioning guide from-scratch # Quick command reference provisioning guide quickstart (_ansi green_bold)VIEWING TIPS(_ansi reset) • (_ansi cyan)Best experience:(_ansi reset) Install glow for beautiful rendering (_ansi default_dimmed)brew install glow # macOS(_ansi reset) • (_ansi cyan)Alternative:(_ansi reset) bat provides syntax highlighting (_ansi default_dimmed)brew install bat # macOS(_ansi reset) • (_ansi cyan)Fallback:(_ansi reset) less/cat work on all systems (_ansi default_dimmed)💡 All guides provide copy-paste ready commands Perfect for quick start and reference!(_ansi reset) " }