# Platform Command Handlers # Handles: platform status, config, health, start, connections, etc. use cli/flags.nu * use platform/mod.nu * use platform/service_manager.nu [ncl-sync-start ncl-sync-stop ncl-sync-status] use platform/user/config.nu [get-platform-config-dir write-provisioning-root] # Main platform command dispatcher export def handle_platform_command [ command: string ops: string flags: record ] { # Parse subcommand from ops if present let parts = ($ops | split row " " | where { |x| ($x | is-not-empty) }) let actual_command = if ($parts | length) > 0 { ($parts | get 0) } else { "" } let remaining_args = if ($parts | length) > 1 { ($parts | skip 1) } else { [] } match $actual_command { "start" => { platform-start $remaining_args $flags } "stop" => { platform-stop $remaining_args } "restart" => { platform-restart $remaining_args $flags } "status" | "st" => { platform-status $remaining_args } "external" | "ext" => { platform-external } "health" => { platform-health } "check" => { platform-check } "list" => { platform-list } "config" => { platform-config } "connections" => { platform-connections } "init" => { platform-init } "logs" | "log" => { platform-logs $remaining_args } "help" | "" => { show-platform-help } _ => { print $"❌ Unknown platform command: ($actual_command)" print "" show-platform-help exit 1 } } } # ============================================================================ # Platform Command Implementations # ============================================================================ def platform-start [args: list, flags: record] { # Persist provisioning root so Rust binaries can resolve schema imports # without requiring $PROVISIONING in their environment. write-provisioning-root # Known deployment modes let known_modes = ["local" "docker" "kubernetes"] # Determine if first arg is a mode or service name let is_mode_spec = ( if ($args | length) > 0 { let first_arg = $args | get 0 $known_modes | any { |m| $m == $first_arg } } else { false } ) # If first arg is NOT a mode, treat all args as service names if (not $is_mode_spec) and ($args | length) > 0 { # ncl-sync doesn't follow the provisioning-* binary convention β€” handle separately. let has_ncl_sync = ($args | any {|s| $s == "ncl-sync" or $s == "ncl_sync"}) if $has_ncl_sync { ncl-sync-start let rest = ($args | where {|s| $s != "ncl-sync" and $s != "ncl_sync"}) if ($rest | is-not-empty) { start-services $rest } } else { start-services $args } print "" platform-status return } # Otherwise, determine mode: from argument or from deployment-mode.ncl let mode = ( if $is_mode_spec { $args | get 0 } else { # Read mode from deployment-mode.ncl let deployment = (load-deployment-mode) $deployment.mode } ) match $mode { "local" => { # Use configuration from deployment-mode.ncl to determine which services to start start-required-services # Show status table after start sleep 3sec print "" platform-status return # Define service registry with correct binary names and startup args # Note: Some services have known issues and are marked as experimental let services_registry = { "vault-service": {port: 8081, binary: "provisioning-vault-service", protocol: "gRPC", args: "--port", status: "stable"} "catalog-registry": {port: 8082, binary: "provisioning-catalog-registry", protocol: "HTTP", args: "--port --host 127.0.0.1", status: "experimental"} "control-center": {port: 8000, binary: "provisioning-control-center", protocol: "HTTP/WebSocket", args: "--port", status: "stable"} "provisioning-rag": {port: 8300, binary: "provisioning-rag", protocol: "REST", args: "--mode solo", status: "stable"} "ai-service": {port: 8083, binary: "provisioning-ai-service", protocol: "HTTP", args: "--port --host 127.0.0.1 --mode solo", status: "stable"} "mcp-server": {port: 8400, binary: "provisioning-mcp-server", protocol: "Binary", args: "", status: "experimental"} "provisioning-nu-daemon": {port: 8100, binary: "provisioning-nu-daemon", protocol: "gRPC", args: "", status: "stable"} "orchestrator": {port: 9090, binary: "provisioning-orchestrator", protocol: "HTTP", args: "--port", status: "stable"} "detector": {port: 8600, binary: "provisioning-detector", protocol: "HTTP", args: "", status: "experimental"} "control-center-ui": {port: 3000, binary: "provisioning-control-center-ui", protocol: "HTTP (WASM)", args: "", status: "missing"} } let service_groups = { "core": ["orchestrator"] "essential": ["vault-service", "provisioning-nu-daemon", "orchestrator"] "stable": ["vault-service", "provisioning-rag", "ai-service", "provisioning-nu-daemon", "orchestrator", "control-center"] "experimental": ["catalog-registry", "mcp-server", "detector"] "all": ["vault-service", "provisioning-rag", "ai-service", "catalog-registry", "mcp-server", "provisioning-nu-daemon", "orchestrator", "control-center", "detector"] } # Determine which services to start let services_set = ($flags | get --optional services | default "core") let services_to_start = ( if $services_set == "all" { $service_groups.all } else if $services_set == "essential" { $service_groups.essential } else if $services_set == "stable" { $service_groups.stable } else if $services_set == "experimental" { $service_groups.experimental } else if $services_set == "custom" { # TODO: Handle custom services from args $service_groups.core } else { $service_groups.core } ) # Display startup information print "" print "πŸš€ Starting Platform Services (Local Binary Mode)" print "═══════════════════════════════════════════════════" print "" print $"πŸ“‹ Service Set: ($services_set)" print $"πŸ”„ Services to start: ($services_to_start | length)" print "" print "Service Sets Available:" print " β€’ core (default): Minimal working setup - orchestrator only" print " β€’ essential: Recommended minimum - vault-service, daemon, orchestrator" print " β€’ stable: All production-ready services" print " β€’ experimental: Experimental/testing services" print " β€’ all: All platform services (including experimental)" print "" # Create logs directory let log_dir = $"($env.HOME? | default "~")/.provisioning/logs" (do { mkdir ($log_dir | path expand) } | ignore) # Start each service let started_count = ($services_to_start | length) let max_index = (($services_to_start | length) - 1) let started_indices = (0..$max_index) for i in $started_indices { let service_name = $services_to_start | get $i let service_info = $services_registry | get $service_name let port = $service_info.port let binary = $service_info.binary let protocol = $service_info.protocol let args_template = $service_info.args let log_file = $"($log_dir | path expand)/($service_name).log" let index_num = ($i + 1) print $" [($index_num)] Starting ($service_name) on port ($port) β€” ($protocol)" # Initialize vault service if needed if $service_name == "vault-service" { let local_bin_dir = ($env.HOME? | default "~" | path expand | path join ".local/bin") let init_script = ($local_bin_dir | path join "provisioning-init-vault") if ($init_script | path exists) { (^bash $init_script out+err> /dev/null | ignore) } else { print $" ⚠️ Vault initialization script not found" } } # Check if binary exists in $HOME/.local/bin let local_bin_dir = ($env.HOME? | default "~" | path expand | path join ".local/bin") let binary_path = ($local_bin_dir | path join $binary) if not ($binary_path | path exists) { print $" βœ— Binary not found: ($binary_path)" print $" β„Ή Install with: just install" } else { # Build command with appropriate arguments let cmd_args = ( if ($args_template | str contains "--port") { # Replace --port placeholder with actual port $args_template | str replace "--port" $"--port ($port)" } else if ($args_template | is-not-empty) { $args_template } else { "" } ) # Add config if available let config_args = ($service_info | get --optional config | default "") let full_args = if ($config_args | is-not-empty) { $"($cmd_args) ($config_args)" } else { $cmd_args } | str trim # Start the service in background using nohup # This properly detaches the process from the terminal # Set up environment variables for specific services let home_expanded = ($env.HOME? | default "~" | path expand) let env_vars = if $service_name == "vault-service" { # Use development mode with Age KMS (can switch to secretumvault in production) $"PROVISIONING_ENV=dev AGE_PUBLIC_KEY_PATH=($home_expanded)/.config/provisioning/age/public_key.txt AGE_PRIVATE_KEY_PATH=($home_expanded)/.config/provisioning/age/private_key.txt" } else if $service_name == "control-center" { $"PROVISIONING_USER_PLATFORM=($home_expanded)/.config/provisioning/platform" } else if $service_name == "mcp-server" { # MCP server needs provisioning path set $"PROVISIONING_PATH=($home_expanded)/Development/provisioning" } else { "" } let start_cmd = if ($env_vars | is-not-empty) { $"nohup env ($env_vars) ($binary_path) ($full_args) >>($log_file) 2>&1 &" } else { $"nohup ($binary_path) ($full_args) >>($log_file) 2>&1 &" } (^sh -c $start_cmd | ignore) sleep 800ms let log_msg = $"logs: ($log_file)" print $" βœ“ Started on port ($port) ($log_msg)" print $" Process may be initializing..." } } print "" print "Service Status:" print $" β€’ Requested to start: ($started_count)" print $" β€’ Logs directory: ($log_dir)" print "" # Show status table after start sleep 1sec print "" platform-status print "Next steps:" print " tail -f $($log_dir)/*.log # Monitor logs in real-time" print " provisioning platform health # Health checks" print " provisioning platform stop # Stop services" print "" print "Note: Services may take time to initialize. Check logs for startup details." } "docker" => { print "🐳 Docker Compose Mode" print " β€’ Start services via docker-compose" print " β€’ Uses docker-compose.yml from deployment config" print "" let provisioning_path = ($env.PROVISIONING? | default "/usr/local/provisioning") let docker_compose_file = $"($provisioning_path)/platform/docker-compose.yml" if ($docker_compose_file | path exists) { print "Starting with docker-compose..." (^docker-compose -f $docker_compose_file up -d out+err> /dev/null | ignore) print "βœ“ Services started" } else { print $"⚠ docker-compose.yml not found at ($docker_compose_file)" print " Create it with: provisioning generate docker-compose" } } "kubernetes" => { print "☸️ Kubernetes Mode" print " β€’ Deploy to Kubernetes cluster" print " β€’ Uses kubectl and manifests" print "" print "TODO: Implement Kubernetes deployment" } _ => { print $"❌ Unknown mode: ($mode)" print "" print "Available modes: local, docker, kubernetes" } } } def platform-stop [args: list] { print "" # If service names are provided, stop only those services if ($args | length) > 0 { # ncl-sync doesn't follow the provisioning-* binary convention β€” handle separately. let has_ncl_sync = ($args | any {|s| $s == "ncl-sync" or $s == "ncl_sync"}) if $has_ncl_sync { ncl-sync-stop print "βœ“ ncl-sync stopped" let rest = ($args | where {|s| $s != "ncl-sync" and $s != "ncl_sync"}) if ($rest | is-not-empty) { stop-services $rest } } else { stop-services $args } platform-status } else { # No service specified - stop all print "πŸ›‘ Stopping All Platform Services" print "═════════════════════════════════" print "" # Kill all provisioning service binaries (excluding this CLI) (^sh -c "pkill -f 'provisioning-[a-z]' 2>/dev/null || true") | ignore ncl-sync-stop sleep 500ms print "βœ“ All services stopped" print "" # Show updated status platform-status } } def platform-restart [args: list, flags: record] { print "" # If a service name is provided, restart only that service if ($args | length) > 0 { let service_name = $args | get 0 # ncl-sync doesn't follow the provisioning-* binary convention β€” handle separately. if $service_name == "ncl-sync" or $service_name == "ncl_sync" { ncl-sync-stop sleep 500ms ncl-sync-start print "" platform-status return } # Normalize: strip "provisioning-" prefix so both "daemon" and "provisioning-daemon" work let svc_short = ($service_name | str replace "_" "-" | if ($in | str starts-with "provisioning-") { str replace "provisioning-" "" } else { $in }) let binary_name = $"provisioning-($svc_short)" let port = (get-service-port $svc_short) # Stop the service (^sh -c $"pkill -f '($binary_name)' 2>/dev/null || true") | ignore sleep 1sec # Start the service print $"β†’ Starting ($service_name)..." let home = ($env.HOME? | default "~" | path expand) let canonical_path = ($home | path join ".local/share/provisioning/bin" $binary_name) let binary_path = if ($canonical_path | path exists) { $canonical_path } else { $home | path join ".local/bin" $binary_name } if not ($binary_path | path exists) { print $"βœ— Binary not found: ($binary_path)" return } let log_dir = ($home | path join ".provisioning/logs") (do { mkdir ($log_dir) } | ignore) let log_file = ($log_dir | path join $"($service_name).log") # Set environment variables for service let platform_path = (get-platform-config-dir) # Properly quote environment variables for shell execution let start_cmd = $"nohup env PROVISIONING_USER_PLATFORM=\"($platform_path)\" PROVISIONING_CONFIG_DIR=\"($platform_path)\" ($binary_path) >>\"($log_file)\" 2>&1 &" (^sh -c $start_cmd | ignore) sleep 2sec let started_msg = $"((ansi green))started((ansi reset))" print $"βœ“ ($service_name) on port ($port) β€” ($started_msg)" print "" # Show updated status platform-status } else { # No service specified - restart all print "πŸ”„ Restarting All Platform Services" print "═════════════════════════════════" print "" # Stop all (^sh -c "pkill -f 'provisioning-[a-z]' 2>/dev/null || true") | ignore print "βœ“ All services stopped" # Wait sleep 2sec # Start all start-required-services # Wait for services to initialize sleep 1sec # Show updated status platform-status } } def platform-status [args: list = []] { let json_out = ("--json" in $args) let clean_args = ($args | where { |a| $a != "--json" }) let filter = ($clean_args | get 0? | default null) let filter_key = if $filter != null { $filter | str replace --all "-" "_" } else { null } let deployment = (load-deployment-mode) let all_services = ( if ($deployment | get --optional "services") != null { $deployment.services | columns } else { [] } ) if ($all_services | length) == 0 and not $json_out { print "⚠ No services found in deployment configuration" return } # ── Platform services ──────────────────────────────────────────────────────── let plat_data = $all_services | each { |service_name| let config = $deployment.services | get $service_name let enabled = ($config | get "enabled" | default false) let port = (get-service-port $service_name) let port_num = (if $port != "?" { $port | into int } else { 0 }) let running = ( if $port_num > 0 { is-port-listening $port_num } else { let bin = $"provisioning-(normalize-service-name $service_name | str replace '_' '-')" (do { ^pgrep -f $bin } | complete).exit_code == 0 } ) { kind: "platform", name: $service_name, port: $port, enabled: $enabled, running: $running, required: $enabled, url: "" } } let ncs = (ncl-sync-status) let plat_with_ncs = ($plat_data | append { kind: "platform", name: "ncl_sync", port: "β€”", enabled: true, running: $ncs.running, required: false, url: "" }) # ── External services ──────────────────────────────────────────────────────── let ext_data = (get-external-services) | each { |svc| let running = (is-port-listening $svc.port) { kind: "external", name: $svc.name, port: ($svc.port | into string), enabled: true, running: $running, required: ($svc.required? | default false), url: $svc.url } } let all_data = ($plat_with_ncs | append $ext_data) # ── Filter by service name ─────────────────────────────────────────────────── let display_data = if $filter_key != null { let matched = ($all_data | where { |r| ($r.name | str replace --all "-" "_") == $filter_key }) if ($matched | is-empty) { if $json_out { print ({error: $"unknown service: ($filter)"} | to json); return } print $"❌ Unknown service: ($filter)"; return } $matched } else { $all_data } # ── JSON output ────────────────────────────────────────────────────────────── if $json_out { print ($display_data | to json) return } # ── Human output ───────────────────────────────────────────────────────────── let table_rows = $display_data | each { |r| let status_str = if $r.running { let label = if $r.enabled { "running" } else { "running*" } $"((ansi green))($label)((ansi reset))" } else { let label = if $r.enabled { "stopped" } else { "disabled" } if $r.enabled { $"((ansi red))($label)((ansi reset))" } else { $"((ansi dark_gray))($label)((ansi reset))" } } let req_str = if $r.required { $"((ansi yellow))req((ansi reset))" } else { "opt" } { Kind: $r.kind, Service: $r.name, Status: $status_str, Port: $r.port, Req: $req_str } } print "" if $filter_key != null { print $"πŸ“Š Status: ($filter)" } else { print "πŸ“Š Platform + External Services" print "════════════════════════════════" } print "" print ($table_rows | table -i false) print "" if $filter_key == null { let running = ($display_data | where running | length) let req_down = ($display_data | where { |r| $r.required and (not $r.running) } | length) print $" βœ“ Running: ($running) / ($display_data | length)" if $req_down > 0 { print $" ❌ Required down: ($req_down) β€” run: prvng plat start" } print "" print "Legend: req=required opt=optional *=running but disabled in config" } } def platform-external [] { print "" print "πŸ”§ External Services (Infrastructure)" print "════════════════════════════════════" print "" let external_services = (get-external-services) if ($external_services | length) == 0 { print "No external services configured" return } # Build external services table let external_data = [ ...($external_services | each { |service| let name = ($service | get "name") let url = ($service | get "url") let port = ($service | get "port") let required = ($service | get "required" | default false) let dependencies = ($service | get "dependencies" | default [] | str join ", ") # Check if service is running by testing if port is listening let is_running = (is-port-listening $port) let status = ( if $is_running { $"((ansi green))running((ansi reset))" } else { $"((ansi red))stopped((ansi reset))" } ) let required_display = ( if $required { $"((ansi red))required((ansi reset))" } else { $"((ansi dark_gray))optional((ansi reset))" } ) { Service: $name, URL: $url, Port: $port, Status: $status, Dependencies: $dependencies, Required: $required_display } }) ] # Display external services table print ($external_data | table -i false) print "" let external_running = ( $external_data | where { |row| ($row.Status | str contains "running") } | length ) let external_total = ($external_data | length) let external_stopped = ($external_total - $external_running) print "Summary:" print $" βœ“ Running: ($external_running)" print $" βœ— Stopped: ($external_stopped)" print "" print "Note: External services are monitored only. Use system commands to manage them." } def platform-health [] { print "" print "πŸ’š Platform Services Health Check" print "═════════════════════════════════" print "" let deployment = (load-deployment-mode) let all_services = $deployment.services | columns mut healthy_count = 0 mut critical_services = [] for service_name in $all_services { let config = $deployment.services | get $service_name let enabled = ($config | get "enabled" | default false) # Load individual service config to get the actual port let port = (get-service-port $service_name) let binary_name = $"provisioning-($service_name | str replace "_" "-")" let is_running = ((^pgrep -f $"[/]($binary_name)$" | complete).exit_code == 0) if $is_running { print $" βœ“ ($service_name) β€” healthy on port ($port)" $healthy_count = ($healthy_count + 1) } else if $enabled { print $" βœ— ($service_name) β€” CRITICAL \(enabled but not running\)" $critical_services = ($critical_services | append $service_name) } else { print $" ⊘ ($service_name) β€” disabled" } } print "" print "Health Summary:" let total = ($all_services | length) let critical_count = ($critical_services | length) print $" βœ“ Running: ($healthy_count) / ($total)" print $" ⚠️ Critical: ($critical_count)" if ($critical_count == 0) { print $" Status: βœ… All enabled services are running" } else { print $" Status: ⚠️ Missing critical services:" for svc in $critical_services { print $" - ($svc)" } } print "" } def platform-list [] { print "" print "πŸ“‹ Available Platform Services" print "═════════════════════════════" print "" let services_info = [ {name: "vault-service", port: 8081, protocol: "gRPC", deps: "none"} {name: "catalog-registry", port: 8082, protocol: "HTTP", deps: "none"} {name: "control-center", port: 8000, protocol: "HTTP/WebSocket", deps: "vault-service"} {name: "provisioning-rag", port: 8300, protocol: "REST", deps: "none"} {name: "ai-service", port: 8083, protocol: "HTTP", deps: "provisioning-rag, vault-service"} {name: "mcp-server", port: 8400, protocol: "Binary", deps: "vault-service"} {name: "provisioning-nu-daemon", port: 8100, protocol: "gRPC", deps: "vault-service"} {name: "orchestrator", port: 9090, protocol: "HTTP", deps: "catalog-registry, control-center, ai-service"} {name: "detector", port: 8600, protocol: "HTTP", deps: "vault-service"} {name: "control-center-ui", port: 3000, protocol: "HTTP (WASM)", deps: "control-center"} ] for svc in $services_info { print $" β€’ ($svc.name)" print $" Port: ($svc.port), Protocol: ($svc.protocol)" print $" Dependencies: ($svc.deps)" print "" } print "Total: 10 services" print "" } def platform-config [] { print "" print "βš™οΈ Platform Configuration" print "════════════════════════" print "" let platform_base = ($env.PROVISIONING_USER_PLATFORM? | default "~/.config/provisioning/platform") print $"Platform Directory: ($platform_base)" print "" print "Configuration Files:" print $" β€’ ($platform_base)/deployment-mode.ncl" print $" β€’ ($platform_base)/config/control-center.ncl" print $" β€’ ($platform_base)/config/orchestrator.ncl" print "" } def platform-connections [] { print "" print "πŸ”— Platform Service Connections" print "════════════════════════════════" print "" print "Service Dependency Graph:" print " vault-service" print " ↓" print " β”œβ”€ control-center" print " β”‚ ↓" print " β”‚ orchestrator" print " β”‚" print " β”œβ”€ ai-service" print " β”‚ ↓" print " β”‚ orchestrator" print " β”‚" print " β”œβ”€ mcp-server" print " └─ provisioning-nu-daemon" print "" print "Service Network Endpoints:" print " β€’ vault-service: grpc://localhost:8081" print " β€’ catalog-registry: http://localhost:8082" print " β€’ control-center: http://localhost:8000" print " β€’ provisioning-rag: http://localhost:8300" print " β€’ ai-service: http://localhost:8083" print " β€’ mcp-server: grpc://localhost:8400" print " β€’ provisioning-nu-daemon: grpc://localhost:8100" print " β€’ orchestrator: http://localhost:9011" print " β€’ detector: http://localhost:8600" print " β€’ control-center-ui: http://localhost:3000" print "" } def platform-init [] { print "" print "πŸ”§ Platform Initialization" print "═════════════════════════" print "" let platform_base = ($env.PROVISIONING_USER_PLATFORM? | default "~/.config/provisioning/platform") print $"Platform Directory: ($platform_base)" print "" if ($"($platform_base)" | path exists) { print "βœ“ Platform directory exists" } else { print "⚠ Platform directory not found" print " Run: setup-platform-config.sh to initialize" } print "" print "Platform is ready for:" print " β€’ provisioning platform start local - Start local services" print " β€’ provisioning platform status - Check service status" print " β€’ provisioning platform health - Health checks" print " β€’ provisioning platform list - List services" print "" } def platform-logs [args: list] { let home = ($env.HOME? | default "~" | path expand) let log_dir = ($home | path join ".provisioning" "logs") # Parse args: first non-numeric token = service name, first numeric token = lines limit let service_arg = ($args | where { |a| not ($a =~ '^[0-9]+$') } | get 0?) let lines_raw = ($args | where { |a| $a =~ '^[0-9]+$' } | get 0?) let lines_arg = if $lines_raw != null { $lines_raw | into int } else { null } if not ($log_dir | path exists) { print "❌ Log directory not found: ~/.provisioning/logs" print " Start services first: provisioning platform start" return } # Resolve initial log file when service name provided upfront let resolved_initial = if $service_arg != null { let exact = ($log_dir | path join $"($service_arg).log") let under = ($log_dir | path join $"($service_arg | str replace --all '-' '_').log") if ($exact | path exists) { $exact } else if ($under | path exists) { $under } else { print "" print $"❌ No log file for: ($service_arg)" print $" Tried: ($exact)" print $" Tried: ($under)" print $" Start with: provisioning platform start ($service_arg)" print "" return } } else { "" } mut keep_going = true mut current_log = $resolved_initial while $keep_going { # Resolve log file for this iteration: preselected or interactive selector let log_file = if ($current_log | is-not-empty) { $current_log } else { let entries = ( ls ($log_dir) | where type == file | where name =~ '\.log$' | get name | each { |f| $f | path basename | str replace --regex '\.log$' '' } ) if ($entries | length) == 0 { print "❌ No log files in ~/.provisioning/logs" $keep_going = false "" } else { let selected = (typedialog select "Service logs:" $entries) $log_dir | path join $"($selected).log" } } if $keep_going and ($log_file | is-not-empty) { let label = ($log_file | path basename | str replace --regex '\.log$' '') print "" print $"πŸ“‹ ($label)" print "─────────────────────────────────────────────────" print "" if $lines_arg != null { ^tail -n $lines_arg ($log_file) } else { ^cat ($log_file) } print "" print "─────────────────────────────────────────────────" print $" ($log_file)" print "" let choice = (typedialog select "ΒΏQuΓ© deseas hacer?" ["Ver otro log" "Salir"]) $current_log = "" if $choice == "Salir" { $keep_going = false } } } } def platform-check [] { print "" print "πŸ” Checking External Services" print "════════════════════════════" print "" # For now, provide template for checking external services # TODO: Load actual config from external-services config file print "External Services to Check:" print " βœ“ Database (SurrealDB/PostgreSQL/Filesystem)" print " βœ“ OCI Registry (Zot/Harbor) for extensions" print " βœ“ Git Source (Forgejo/Gitea/GitHub) for discovery" print " βœ“ Cache Service (Local directory or Redis)" print "" print "To implement full checks, ensure config is loaded from:" print " β€’ PLATFORM_MODE environment variable" print " β€’ Workspace config/platform/deployment-mode.ncl" print " β€’ System defaults" print "" print "Remediation:" print " 1. Set deployment mode: export PLATFORM_MODE=solo|multiuser|enterprise" print " 2. Configure external services in platform config" print " 3. Run 'provisioning platform check' again" print "" } def show-platform-help [] { print "" print "πŸ–₯️ Platform Commands" print "====================" print "" print " platform start [mode|service] - Start services (mode from deployment-mode.ncl if omitted)" print " platform stop [service] - Stop all services or specific service" print " platform restart [service] - Restart all services or specific service" print " platform status - Show service status" print " platform health - Health checks" print " platform external - Show external services status" print " platform list - List available services" print " platform config - Show configuration" print " platform connections - Show service connections" print " platform logs [service] - Stream service logs (interactive selector if no service given)" print " platform init - Initialize platform" print "" print "Examples:" print " provisioning platform start # Start using deployment-mode" print " provisioning platform start local --services core # Override mode" print " provisioning platform start vault_service # Start single service" print " provisioning platform stop orchestrator # Stop single service" print " provisioning platform restart vault_service # Restart single service" print " provisioning platform status" print " provisioning platform health" print " provisioning platform external # Check external services" print " provisioning platform logs # Interactive selector β†’ show full log β†’ loop" print " provisioning platform logs orchestrator # Show full orchestrator log" print " provisioning platform logs orchestrator 50 # Show last 50 lines" print " prvng p logs 100 # Interactive selector, last 100 lines" print "" }