# Runner daemon handler — list/destroy/pin/unpin/status via lian-build daemon HTTP API. const DAEMON_DEFAULT_URL = "http://localhost:14150" def daemon-url []: nothing -> string { $env.LIAN_BUILD_DAEMON_URL? | default $DAEMON_DEFAULT_URL } def format-runner [r: record]: nothing -> record { let ttl = if $r.lifecycle.ttl_minutes == 0 { "—" } else { $"($r.lifecycle.ttl_minutes)m" } let sched = $r.lifecycle.destroy_at? | default "—" { id: ($r.id | str substring 0..<8) workspace: $r.workspace host: $"($r.ssh_host):($r.ssh_port)" status: $r.status pin: $r.lifecycle.pin ttl: $ttl destroy_at: $sched spawned_at: $r.spawned_at } } def runner-list [base_url: string]: nothing -> nothing { let rows = try { http get $"($base_url)/runners" } catch { print $"error: daemon unreachable at ($base_url)" print " Start with: lian-build serve" return } if ($rows | is-empty) { print "no active runners" return } $rows | each { |r| format-runner $r } | table } def runner-status [base_url: string, id: string]: nothing -> nothing { if ($id | is-empty) { print "error: runner status requires an id (full UUID or 8-char prefix)" exit 1 } let rows = try { http get $"($base_url)/runners" } catch { print $"error: daemon unreachable at ($base_url)" print " Start with: lian-build serve" return } let matches = $rows | where { |r| ($r.id | str starts-with $id) } if ($matches | is-empty) { print $"error: no runner matching id '($id)'" exit 1 } $matches | first | table } def runner-destroy [base_url: string, id: string, confirmed: bool]: nothing -> nothing { if not $confirmed { print $"Destroy runner ($id)? This cannot be undone." let answer = (input "Confirm [y/N]: " | str downcase | str trim) if $answer != "y" { print "aborted" return } } let result = try { http delete $"($base_url)/runners/($id)" { ok: true } } catch { |e| { ok: false, error: ($e | get msg? | default "request failed") } } if $result.ok { print $"runner ($id) queued for destruction" } else { print $"error: ($result.error)" exit 1 } } def runner-set-pin [base_url: string, id: string, pin: bool]: nothing -> nothing { let endpoint = if $pin { "pin" } else { "unpin" } let result = try { http post $"($base_url)/runners/($id)/($endpoint)" {} { ok: true } } catch { |e| { ok: false, error: ($e | get msg? | default "request failed") } } if $result.ok { let verb = if $pin { "pinned" } else { "unpinned" } print $"runner ($id) ($verb)" } else { print $"error: ($result.error)" exit 1 } } def runner-help []: nothing -> nothing { print "Runner Daemon Commands" print "======================" print "" print "Usage: provisioning runner [options]" print "" print "Commands:" print " list List active runners" print " status Show full record for a runner (prefix match)" print " destroy Destroy a runner (prompts unless --yes)" print " pin Pin runner — daemon will not auto-destroy it" print " unpin Unpin runner — daemon resumes TTL/schedule enforcement" print "" print "Environment:" print $" LIAN_BUILD_DAEMON_URL Daemon base URL (default: ($DAEMON_DEFAULT_URL))" print "" print "Examples:" print " provisioning runner list" print " provisioning runner status a1b2c3d4" print " provisioning runner destroy a1b2c3d4 --yes" print " provisioning runner pin a1b2c3d4" } export def handle_runner_command [ command: string ops: string flags: record ]: nothing -> nothing { let url = daemon-url let id = ($ops | str trim) let confirmed = ($flags.auto_confirm? | default false) match $command { "list" | "l" => { runner-list $url } "status" | "s" => { runner-status $url $id } "destroy" | "d" => { if ($id | is-empty) { print "error: runner destroy requires an id" exit 1 } runner-destroy $url $id $confirmed } "pin" | "p" => { if ($id | is-empty) { print "error: runner pin requires an id" exit 1 } runner-set-pin $url $id true } "unpin" | "u" => { if ($id | is-empty) { print "error: runner unpin requires an id" exit 1 } runner-set-pin $url $id false } _ => { runner-help } } }