#!/usr/bin/env nu # provisioning-daemon lifecycle — start, stop, status, logs, restart. # Targets the new HTTP+NATS daemon (provisioning-daemon binary, port 9014). const DAEMON_BINARY = "provisioning-daemon" const DAEMON_BIND = "0.0.0.0:9014" const HEALTH_URL = "http://127.0.0.1:9014/health" const PID_FILE = "/tmp/provisioning-daemon.pid" const LOG_FILE = "/tmp/provisioning-daemon.log" const STARTUP_WAIT_S = 5 # seconds to poll before giving up def read-pid []: nothing -> string { if ($PID_FILE | path exists) { open $PID_FILE | str trim } else { "" } } def pid-alive [pid: string]: nothing -> bool { if ($pid | is-empty) { return false } let r = (do { ^kill -0 ($pid | into int) } | complete) $r.exit_code == 0 } def write-pid [pid: string]: nothing -> nothing { $pid | save --force $PID_FILE } def remove-pid []: nothing -> nothing { if ($PID_FILE | path exists) { rm $PID_FILE } } def wait-healthy []: nothing -> bool { let attempts = (seq 1 ($STARTUP_WAIT_S * 2)) let ok = ($attempts | any {|_| let r = (do { ^curl -sf $HEALTH_URL } | complete) if $r.exit_code == 0 { true } else { sleep 500ms; false } }) $ok } def start-cmd []: nothing -> nothing { let pid = (read-pid) if (pid-alive $pid) { print $"provisioning-daemon already running \(PID ($pid)\)" return } let log_dir = ($LOG_FILE | path dirname) if not ($log_dir | path exists) { ^mkdir -p $log_dir } # Resolve binary — check release build first, then PATH let repo_root = (do { ^git rev-parse --show-toplevel } | complete) let release = ( if $repo_root.exit_code == 0 { $"($repo_root.stdout | str trim)/provisioning/platform/target/release/($DAEMON_BINARY)" } else { "" } ) let bin_path = (if (not ($release | is-empty)) and ($release | path exists) { $release } else { $DAEMON_BINARY }) # Resolve the repo root for NICKEL_IMPORT_PATH and config path. let repo_provisioning = ( if $repo_root.exit_code == 0 { $"($repo_root.stdout | str trim)/provisioning" } else { "" } ) let ontoref_path = "/Users/Akasha/Development/ontoref" let nickel_ip = ( if not ($repo_provisioning | is-empty) { if ($ontoref_path | path exists) { $"($repo_provisioning):($ontoref_path)" } else { $repo_provisioning } } else { "" } ) let config_file = $"($env.HOME)/Library/Application Support/provisioning/platform/config/provisioning-daemon.ncl" let config_arg = (if ($config_file | path exists) { $"--config '($config_file)'" } else { "" }) let env_prefix = (if not ($nickel_ip | is-empty) { $"NICKEL_IMPORT_PATH='($nickel_ip)'" } else { "" }) let daemon_pid = (do { ^bash -c $"($env_prefix) nohup ($bin_path) ($config_arg) >> ($LOG_FILE) 2>&1 & echo $!" } | complete) if $daemon_pid.exit_code != 0 { print $"Failed to start daemon: ($daemon_pid.stderr)" return } let pid_str = ($daemon_pid.stdout | str trim) write-pid $pid_str if (wait-healthy) { print $"provisioning-daemon started \(PID ($pid_str)\) → ($HEALTH_URL)" } else { print $"provisioning-daemon did not become healthy in ($STARTUP_WAIT_S)s — check ($LOG_FILE)" } } def stop-cmd []: nothing -> nothing { let pid = (read-pid) if not (pid-alive $pid) { print "provisioning-daemon is not running" return } let r = (do { ^kill ($pid | into int) } | complete) if $r.exit_code != 0 { print $"Failed to send SIGTERM: ($r.stderr)" return } sleep 500ms if (pid-alive $pid) { let r2 = (do { ^kill -9 ($pid | into int) } | complete) if $r2.exit_code != 0 { print "Force-kill failed"; return } } remove-pid print "provisioning-daemon stopped" } def status-cmd []: nothing -> nothing { let pid = (read-pid) if not (pid-alive $pid) { print "provisioning-daemon is not running" return } let r = (do { ^curl -sf $HEALTH_URL } | complete) if $r.exit_code == 0 { let h = ($r.stdout | from json) print $"provisioning-daemon running \(PID ($pid)\)" print $" status: ($h.status)" print $" tools: ($h.tools)" print $" calls: ($h.invocations)" } else { print $"provisioning-daemon running \(PID ($pid)\) but not responding at ($HEALTH_URL)" } } def logs-cmd []: nothing -> nothing { if not ($LOG_FILE | path exists) { print $"Log file not found: ($LOG_FILE)" return } ^tail -n 100 $LOG_FILE } def restart-cmd []: nothing -> nothing { stop-cmd sleep 1sec start-cmd } # ── External services: jj + Radicle ────────────────────────────────────────── def jj-init-workspace [ws_root: string]: nothing -> nothing { let jj_dir = ($ws_root | path join ".jj") if ($jj_dir | path exists) { print $" [✓] jj already initialized at ($ws_root)" return } let r = (do { ^jj git init --colocate $ws_root } | complete) if $r.exit_code == 0 { print $" [+] jj initialized at ($ws_root)" } else { print $" [!] jj init failed: ($r.stderr | str trim)" } } def rad-ensure-running [passphrase: string = ""]: nothing -> nothing { let status_r = (do { ^rad node status } | complete) let is_running = ($status_r.exit_code == 0 and not ($status_r.stdout | str contains "stopped")) if $is_running { print " [✓] Radicle node already running" return } let env_extra = if ($passphrase | is-not-empty) { { RAD_PASSPHRASE: $passphrase } } else { {} } let start_r = (do { with-env $env_extra { ^rad node start } } | complete) if $start_r.exit_code == 0 { print " [+] Radicle node started" } else { let err = $start_r.stderr | str trim if ($err | str contains "Passphrase") or ($err | str contains "passphrase") { print " [!] Radicle keystore locked — set RAD_PASSPHRASE env var before starting services" } else { print $" [!] rad node start failed: ($err)" } } } def services-start-cmd [ws_root: string, rad_passphrase: string]: nothing -> nothing { print "── External services ─────────────────────────────────────────" let target_ws = if ($ws_root | is-not-empty) { $ws_root } else { $env.PWD } if (^which jj | complete).exit_code == 0 { jj-init-workspace $target_ws } else { print " [skip] jj not found in PATH" } if (^which rad | complete).exit_code == 0 { rad-ensure-running $rad_passphrase } else { print " [skip] rad not found in PATH" } } def services-status-cmd []: nothing -> nothing { print "── External services status ───────────────────────────────────" let jj_r = (do { ^jj --version } | complete) print (if $jj_r.exit_code == 0 { $" jj : ($jj_r.stdout | str trim)" } else { " jj : not found" }) let rad_r = (do { ^rad node status } | complete) let rad_status = if $rad_r.exit_code == 0 { if ($rad_r.stdout | str contains "stopped") { "stopped" } else { "running" } } else { "not found" } print $" rad : ($rad_status)" } def main [ command: string = "help" --workspace (-w): string = "" --rad-passphrase: string = "" ]: nothing -> nothing { match $command { "start" => { start-cmd } "stop" => { stop-cmd } "status" => { status-cmd } "logs" => { logs-cmd } "restart" => { restart-cmd } "services" => { services-start-cmd $workspace $rad_passphrase } "services-status" => { services-status-cmd } _ => { print "Usage: start-provisioning-daemon.nu " print "" print " start Start provisioning-daemon" print " stop Stop provisioning-daemon" print " status Health check" print " logs Tail daemon log" print " restart Stop + start" print " services Init jj workspace + start Radicle node" print " services-status Show jj + Radicle status" print "" print " --workspace (-w) Workspace root for jj init (default: PWD)" print " --rad-passphrase Radicle keystore passphrase" } } }