provisioning-core/nulib/cli/handlers/platform.nu

891 lines
34 KiB
Text
Raw Permalink Normal View History

# 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<string>, 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<string>] {
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<string>, 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<string> = []] {
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<string>] {
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 ""
}