# Extensions Management Commands use ../lib_provisioning/extensions * use ../lib_provisioning/config/accessor.nu * use ../lib_provisioning/utils/nickel_processor.nu [ncl-eval-soft, default-ncl-paths] # Resolve the taskservs directory: PROVISIONING_TASKSERVS_PATH → config → $PROVISIONING/extensions/taskservs. def resolve-taskservs-dir [] : nothing -> string { let from_env = ($env.PROVISIONING_TASKSERVS_PATH? | default "") if ($from_env | is-not-empty) { return $from_env } let from_config = (get-taskservs-path) if ($from_config | is-not-empty) { return $from_config } ($env.PROVISIONING? | default "/usr/local/provisioning") | path join "extensions" "taskservs" } # Load metadata.ncl for each taskserv via nickel export and aggregate provides/requires/conflicts_with. def load-taskserv-capabilities [] : nothing -> list { let ts_dir = (resolve-taskservs-dir) if not ($ts_dir | path exists) { return [] } glob ($ts_dir | path join "*") | where ($it | path type) == "dir" | each { |ts_path| let meta_path = ($ts_path | path join "metadata.ncl") if not ($meta_path | path exists) { null } else { let prov = ($env.PROVISIONING? | default "/usr/local/provisioning") let m = (ncl-eval-soft $meta_path (default-ncl-paths "") null) if ($m | is-not-empty) { { name: $m.name, version: $m.version, description: $m.description, provides: ($m.provides? | default []), requires: ($m.requires? | default []), conflicts_with: ($m.conflicts_with? | default []), } } else { null } } } | where ($it != null) } # List available extensions export def "main extensions list" [ --type: string = "" # Filter by type: provider, taskserv, or all --helpinfo (-h) # Show help ] { if $helpinfo { print "List available extensions" return } match $type { "provider" => { print "Available Provider Extensions:" list-providers } "taskserv" => { print "Available TaskServ Extensions:" list-taskservs } _ => { print "Available Extensions:" print "\nProviders:" list-providers print "\nTaskServs:" list-taskservs } } } # Show extension details export def "main extensions show" [ name: string # Extension name --helpinfo (-h) # Show help ] { if $helpinfo { print "Show details for a specific extension" return } let provider = (get-provider $name) let taskserv = (get-taskserv $name) if ($provider | is-not-empty) { print $"Provider Extension: ($name)" $provider } else if ($taskserv | is-not-empty) { print $"TaskServ Extension: ($name)" $taskserv } else { print $"Extension '($name)' not found" } } # Initialize extensions export def "main extensions init" [ --helpinfo (-h) # Show help ] { if $helpinfo { print "Initialize extension registry" return } init-registry print "Extension registry initialized" } # Show current profile export def "main profile show" [ --helpinfo (-h) # Show help ] { if $helpinfo { print "Show current access profile" return } show-profile | table } # Create example profiles export def "main profile create-examples" [ --helpinfo (-h) # Show help ] { if $helpinfo { print "Create example profile files" return } create-example-profiles } # List capability declarations across all taskservs (provides + requires). export def "main extensions capabilities" [ --type (-t): string = "all" # Filter: "provides", "requires", or "all" --helpinfo (-h) # Show help ] : nothing -> any { if $helpinfo { print "List capability declarations across all taskservs" print " --type: provides | requires | all (default: all)" return } let caps = (load-taskserv-capabilities) if ($caps | is-empty) { print "No taskservs found or metadata.ncl missing." return } match $type { "provides" => { $caps | each { |ts| $ts.provides | each { |p| { taskserv: $ts.name, provides_id: $p.id, version: $p.version, interface: $p.interface } } } | flatten | table } "requires" => { $caps | each { |ts| $ts.requires | each { |r| { taskserv: $ts.name, capability: $r.capability, kind: $r.kind } } } | flatten | table } _ => { $caps | each { |ts| { taskserv: $ts.name, provides: ($ts.provides | each { |p| $p.id } | str join ", "), requires: ($ts.requires | each { |r| $"($r.capability)[($r.kind)]" } | str join ", "), conflicts_with: ($ts.conflicts_with | str join ", "), } } | table } } } # Show inter-extension dependency graph derived from provides/requires metadata. export def "main extensions graph" [ --format (-f): string = "table" # Output format: table, dot --helpinfo (-h) # Show help ] : nothing -> any { if $helpinfo { print "Show inter-extension dependency graph from provides/requires metadata" print " --format: table | dot (default: table)" return } let caps = (load-taskserv-capabilities) if ($caps | is-empty) { print "No taskservs found." return } # Build provides index: capability_id -> taskserv name let provides_index = ($caps | each { |ts| $ts.provides | each { |p| { cap: $p.id, provider: $ts.name } } } | flatten) # Build edges: (requirer, capability, provider, kind) let edges = ($caps | each { |ts| $ts.requires | each { |r| let provider = ($provides_index | where cap == $r.capability | get provider?.0 | default "unresolved") { from: $ts.name, capability: $r.capability, to: $provider, kind: $r.kind } } } | flatten) match $format { "table" => { $edges | table } "dot" => { print "digraph extensions {" print " rankdir=LR;" for edge in $edges { let style = if $edge.kind == "Required" { "" } else { " style=dashed" } print $" \"($edge.from)\" -> \"($edge.to)\" [label=\"($edge.capability)\"($style)];" } print "}" } _ => { error make { msg: $"Unknown format '($format)'. Valid: table, dot" } } } }