provisioning-core/nulib/cli/handlers/utilities/shell.nu

108 lines
4.8 KiB
Text

# Shell Command Handlers
# Domain: Nushell environment, shell info, and resource listing
# REMOVED: use ../../../lib_provisioning * - causes circular import
use cli/flags.nu *
# Validate infrastructure name is safe from path injection
def validate_infra_name [infra: string] {
# Returns true if INVALID (contains dangerous patterns)
if ($infra | str contains "/") or ($infra | str contains "..") or ($infra | str starts-with "/") or ($infra | str contains " ") {
return true
}
false
}
# Nu shell command handler - Start Nushell with provisioning library loaded
export 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) {
# Validate infra name to prevent path injection
if (validate_infra_name $flags.infra) {
error make { msg: "Invalid infrastructure name - contains path traversal characters" }
}
if ($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)"
}
}
}
}
# Nu info command handler - Show Nushell version info
export def handle_nuinfo [] {
print $"\n (_ansi yellow)Nu shell info(_ansi reset)"
print (version)
}
# List command handler - List resources (servers, taskservs, clusters)
export 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
}