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

890 lines
34 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 ""
}