# Command Dispatcher # Central routing logic for all provisioning commands use flags.nu * use commands/infrastructure.nu * use commands/orchestration.nu * use commands/development.nu * use commands/workspace.nu * use commands/generation.nu * use commands/utilities.nu * use commands/configuration.nu * use commands/guides.nu * use commands/authentication.nu * use commands/diagnostics.nu * use commands/integrations.nu * use commands/vm_domain.nu * use commands/platform.nu * use ../lib_provisioning * use ../lib_provisioning/workspace/enforcement.nu * use ../lib_provisioning/commands/traits.nu * use ./flags.nu extract-workspace-infra-from-flags use ./metadata_handler.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 } } # Command registry with shortcuts and aliases # Maps short forms and aliases to their canonical command domain export def get_command_registry []: nothing -> record { { # Infrastructure commands (server, taskserv, cluster, infra) "s": "infrastructure server" "server": "infrastructure server" "t": "infrastructure taskserv" "task": "infrastructure taskserv" "taskserv": "infrastructure taskserv" "cl": "infrastructure cluster" "cluster": "infrastructure cluster" "i": "infrastructure infra" "infra": "infrastructure infra" "infras": "infrastructure infra" # VM commands (vm, hosts, lifecycle) "vm": "vm vm" "vmi": "vm info" "vmh": "vm hosts" "vml": "vm lifecycle" "vm-create": "vm create" "vm-list": "vm list" "vm-start": "vm start" "vm-stop": "vm stop" "vm-delete": "vm delete" "vm-hosts-check": "vm hosts check" "vm-hosts-prepare": "vm hosts prepare" # Orchestration commands (workflow, batch, orchestrator) "wf": "orchestration workflow" "flow": "orchestration workflow" "workflow": "orchestration workflow" "bat": "orchestration batch" "batch": "orchestration batch" "orch": "orchestration orchestrator" "orchestrator": "orchestration orchestrator" # Development commands (module, layer, version, pack) "mod": "development module" "module": "development module" "lyr": "development layer" "layer": "development layer" "version": "development version" "pack": "development pack" # Module discover shortcuts "discover": "development module discover" "disc": "development module discover" "discover-taskservs": "development module discover taskservs" "disc-t": "development module discover taskservs" "dt": "development module discover taskservs" "discover-providers": "development module discover providers" "disc-p": "development module discover providers" "dp": "development module discover providers" "discover-clusters": "development module discover clusters" "disc-c": "development module discover clusters" "dc": "development module discover clusters" # Workspace commands (workspace, template) "ws": "workspace workspace" "workspace": "workspace workspace" "tpl": "workspace template" "tmpl": "workspace template" "template": "workspace template" # Platform commands (platform, orchestrator, control-center) "plat": "platform platform" "platform": "platform platform" # Configuration commands (env, allenv, show, init, validate) "e": "config env" "env": "config env" "allenv": "config allenv" "show": "config show" "init": "config init" "validate": "config validate" "val": "config validate" "config-template": "config config-template" # Authentication commands (auth, login, logout, mfa) - mapped to integrations for plugin support "login": "integrations auth login" "logout": "integrations auth logout" "whoami": "integrations auth verify" "mfa": "authentication mfa" "mfa-enroll": "authentication mfa-enroll" "mfa-verify": "authentication mfa-verify" # Utility commands (sed, sops, cache, providers, etc.) "sed": "utils sed" "sops": "utils sops" "cache": "utils cache" "providers": "utils providers" "nu": "utils nu" # Test environment commands "test": "test" "tst": "test" "list": "utils list" "l": "utils list" "ls": "utils list" "qr": "utils qr" "nuinfo": "utils nuinfo" "plugin": "utils plugin" "plugins": "utils plugins" "plugin-list": "utils plugin list" "plugin-add": "utils plugin register" "plugin-test": "utils plugin test" # Generation and Infrastructure-from-Code commands "g": "generation generate" "gen": "generation generate" "generate": "generation generate" "detect": "generation detect" "complete": "generation complete" "ifc": "generation workflow" # Guide commands (avoiding conflicts with existing infrastructure commands) "guide": "guides guide" "guides": "guides guide" "sc": "guides sc" "shortcuts": "guides sc" "quickstart": "guides quickstart" "quick": "guides quickstart" "from-scratch": "guides from-scratch" "scratch": "guides from-scratch" "customize": "guides customize" "custom": "guides customize" "howto": "guides guide list" # Diagnostics commands "status": "diagnostics status" "health": "diagnostics health" "next": "diagnostics next" "phase": "diagnostics phase" # Plugin-powered commands (10-30x faster with native plugins) "auth": "integrations auth" "auth-login": "integrations auth login" "auth-logout": "integrations auth logout" "auth-verify": "integrations auth verify" "kms": "integrations kms" "kms-encrypt": "integrations kms encrypt" "kms-decrypt": "integrations kms decrypt" "kms-status": "integrations kms status" "encrypt": "integrations kms encrypt" "decrypt": "integrations kms decrypt" "orch-status": "integrations orch status" "orch-tasks": "integrations orch tasks" # Integrations commands (prov-ecosystem + provctl) "int": "integrations integrations" "integ": "integrations integrations" "integrations": "integrations integrations" "runtime": "integrations runtime" "ssh-pool": "integrations ssh" "ssh": "integrations ssh" "backup": "integrations backup" "gitops": "integrations gitops" "service": "integrations service" # Special commands (handled separately) "h": "help" "c": "infrastructure create" "create": "infrastructure create" "d": "infrastructure delete" "delete": "infrastructure delete" "u": "infrastructure update" "update": "infrastructure update" "price": "price" "prices": "price" "cost": "price" "costs": "price" "cst": "create-server-task" "create-server-task": "create-server-task" "csts": "create-server-task" "create-servers-tasks": "create-server-task" "deploy-rm": "deploy" "deploy-del": "deploy" "dp-rm": "deploy" "d-r": "deploy" "destroy": "deploy" "deploy-sel": "deploy-sel" "deploy-list": "deploy-sel" "dp-sel": "deploy-sel" "d-s": "deploy-sel" "deploy-sel-tree": "deploy-sel-tree" "deploy-list-tree": "deploy-sel-tree" "dp-sel-t": "deploy-sel-tree" "d-st": "deploy-sel-tree" "new": "new" "ai": "ai" "context": "context" "ctx": "context" "setup": "setup" "st": "setup" "config": "setup" "control-center": "control-center" "mcp-server": "mcp-server" } } # Main command dispatcher # Routes commands to appropriate domain handlers export def dispatch_command [ args: list flags: record ] { let task = if ($args | length) > 0 { ($args | get 0) } else { "" } let ops_list = ($args | skip 1) let ops_str = ($ops_list | str join " ") # Handle empty command if ($task | is-empty) { print "Use 'provisioning help' for available commands" exit } # Handle "provisioning help " directly # This is critical for commands like "provisioning help workspace" if $task in ["help" "h"] { let category = if ($ops_list | length) > 0 { ($ops_list | get 0) } else { "" } exec $"($env.PROVISIONING_NAME)" help $category --notitles } # Intercept bi-directional help: "provisioning help" → "provisioning help " # This ensures shortcuts like "provisioning ws help" work correctly let first_op = if ($ops_list | length) > 0 { ($ops_list | get 0) } else { "" } if $first_op in ["help" "h"] { # Redirect to categorized help system exec $"($env.PROVISIONING_NAME)" help $task --notitles } # Resolve command through registry let registry = get_command_registry let resolved = if ($task in ($registry | columns)) { $registry | get $task } else { $task } # Split into domain, command, and optional subcommand args let parts = ($resolved | split row " ") let domain = if ($parts | length) > 1 { ($parts | get 0) } else { "special" } let command = if ($parts | length) > 1 { ($parts | get 1) } else { $task } # Extract any additional parts as pre-filled ops (for compound shortcuts like "dt" → "discover taskservs") let extra_ops = if ($parts | length) > 2 { ($parts | skip 2 | str join " ") } else { "" } # Combine extra_ops with user-provided ops let final_ops = if ($extra_ops | is-not-empty) and ($ops_str | is-not-empty) { $"($extra_ops) ($ops_str)" } else if ($extra_ops | is-not-empty) { $extra_ops } else { $ops_str } # Handle workspace override from flags let workspace_context = (extract-workspace-infra-from-flags $flags) # Set temporary workspace context if specified if ($workspace_context.workspace | is-not-empty) { $env.TEMP_WORKSPACE = $workspace_context.workspace } # Update infra flag if parsed from workspace:infra notation let updated_flags = if ($workspace_context.infra | is-not-empty) { $flags | merge { infra: $workspace_context.infra } } else { $flags } # WORKSPACE ENFORCEMENT - Check workspace requirement before processing # This enforces that most commands require an active workspace let enforcement_allowed = (check-and-enforce $task $args) if not $enforcement_allowed { # Enforcement failed - error already displayed by check-and-enforce exit 1 } # METADATA VALIDATION - Check command requirements and handle interactive forms # Build canonical command name for metadata lookup let canonical_name = if ($domain == "special") { $command } else if ($domain != "") { $"($domain) ($command)" } else { $command } # Validate command and prepare execution (handles interactive forms) let prep_result = (validate-and-prepare $canonical_name $updated_flags) if not $prep_result.proceed { # Validation failed - error already displayed by validate-and-prepare exit 1 } # Set environment based on flags set_debug_env $updated_flags # Ensure PROVISIONING_INFRA is explicitly set if infra flag was provided # This ensures context-aware filtering works with --infra flag if ($updated_flags.infra | is-not-empty) { $env.PROVISIONING_INFRA = $updated_flags.infra } # Dispatch to domain handler match $domain { "infrastructure" => { handle_infrastructure_command $command $final_ops $updated_flags } "orchestration" => { handle_orchestration_command $command $final_ops $updated_flags } "development" => { handle_development_command $command $final_ops $updated_flags } "workspace" => { handle_workspace_command $command $final_ops $updated_flags } "config" => { handle_config_command $command $final_ops $updated_flags } "utils" => { handle_utility_command $command $final_ops $updated_flags } "generation" => { handle_generation_command $command $final_ops $updated_flags } "guides" => { handle_guide_command $command $final_ops $updated_flags } "authentication" => { handle_authentication_command $command $final_ops $updated_flags } "diagnostics" => { handle_diagnostics_command $command $final_ops $updated_flags } "integrations" => { handle_integrations_command $command $final_ops $updated_flags } "platform" => { handle_platform_command $command $final_ops $updated_flags } "vm" => { handle_vm_command $command $final_ops $updated_flags } "special" => { handle_special_command $command $final_ops $updated_flags } "test" => { handle_test_command $command $final_ops $updated_flags } "help" => { exec $"($env.PROVISIONING_NAME)" help $command --notitles } _ => { invalid_task "" $task --end exit 1 } } # Clean up temporary workspace context if ($workspace_context.workspace | is-not-empty) { hide-env TEMP_WORKSPACE } } # Integrations command handler (prov-ecosystem + provctl) def handle_integrations_command [command: string, ops: string, flags: record] { let args_list = if ($ops | is-not-empty) { $ops | split row " " | where { |x| ($x | is-not-empty) } } else { [] } let check_mode = if $flags.check_mode { "--check" } else { "" } # Parse command - could be "integrations integrations" or just the subcommand let subcommand = if $command == "integrations" { ($args_list | get 0?) } else { $command } # Get remaining args let remaining_args = if $command == "integrations" { ($args_list | skip 1) } else { $args_list } # Call the integrations handler with parsed arguments if ($subcommand == null) { cmd-integrations "help" $remaining_args --check=($check_mode | is-not-empty) } else { cmd-integrations $subcommand $remaining_args --check=($check_mode | is-not-empty) } } # Test command handler def handle_test_command [command: string, ops: string, flags: record] { let args = if ($ops | is-not-empty) { $ops } else { "" } run_module $args "test" --exec } # Special command handler (create, delete, update, deploy, etc.) def handle_special_command [command: string, ops: string, flags: record] { match $command { "create" | "c" => { let use_debug = if $flags.debug_mode or ($env.PROVISIONING_DEBUG? | default false) { "-x" } else { "" } let use_check = if $flags.check_mode { "--check " } else { "" } let str_infra = if ($flags.infra | is-not-empty) { $"--infra ($flags.infra) " } else { "" } let str_out = if ($flags.outfile | is-not-empty) { $"--outfile ($flags.outfile) " } else { "" } exec $"($env.PROVISIONING_NAME)" $use_debug "create" $ops $use_check $str_infra $str_out --notitles } "delete" | "d" => { let use_debug = if $flags.debug_mode { "-x" } else { "" } let use_check = if $flags.check_mode { "--check " } else { "" } let use_yes = if $flags.auto_confirm { "--yes " } else { "" } let use_keepstorage = if $flags.keep_storage { "--keepstorage " } else { "" } let str_infra = if ($flags.infra | is-not-empty) { $"--infra ($flags.infra) " } else { "" } exec $"($env.PROVISIONING_NAME)" "delete" $ops $use_check $use_yes $use_keepstorage $str_infra --notitles } "update" | "u" => { let use_debug = if $flags.debug_mode { "-x" } else { "" } let use_check = if $flags.check_mode { "--check " } else { "" } let str_infra = if ($flags.infra | is-not-empty) { $"--infra ($flags.infra) " } else { "" } exec $"($env.PROVISIONING_NAME)" "update" $ops $use_check $str_infra --notitles } "price" | "prices" | "cost" | "costs" => { handle_price_command $ops $flags } "create-server-task" | "cst" | "csts" | "create-servers-tasks" => { handle_create_server_task $ops $flags } "new" => { let str_new = ($flags.new_infra | default "") print $"\n (_ansi yellow)New Infra ($str_new)(_ansi reset)" } "ai" => { let str_infra = if ($flags.infra | is-not-empty) { $"--infra ($flags.infra) " } else { "" } let str_settings = if ($flags.settings | is-not-empty) { $"--settings ($flags.settings) " } else { "" } let str_out = if ($flags.output_format | is-not-empty) { $"--out ($flags.output_format) " } else { "" } run_module $"($ops) ($str_infra) ($str_settings) ($str_out)" "ai" --exec } "context" | "ctx" => { ^$"($env.PROVISIONING_NAME)" "context" $ops --notitles run_module $ops "" --exec } "setup" | "st" | "config" => { # Route to full setup command handler use commands/setup.nu * let args_list = if ($ops | is-not-empty) { $ops | split row " " | where { |x| ($x | is-not-empty) } } else { [] } let command = if ($args_list | length) > 0 { ($args_list | get 0) } else { "help" } let remaining_args = if ($args_list | length) > 1 { ($args_list | skip 1) } else { [] } cmd-setup $command $remaining_args --check=$flags.check_mode --verbose=$flags.debug_mode --yes=$flags.auto_confirm } "control-center" => { run_module $ops "control-center" --exec } "mcp-server" => { run_module $ops "mcp-server" --exec } _ => { print $"❌ Unknown command: ($command)" print "Use 'provisioning help' for available commands" exit 1 } } }