#!/usr/bin/env nu # Provider Discovery System # Discovers available cloud providers with metadata extraction use ../lib_provisioning/config/accessor.nu config-get # Discover all available providers export def discover-providers []: nothing -> list { # Get absolute path to extensions directory from config let providers_path = (config-get "paths.providers" | path expand) if not ($providers_path | path exists) { error make { msg: $"Providers path not found: ($providers_path)" } } # Find all provider directories with KCL modules ls $providers_path | where type == "dir" | each { |dir| let provider_name = ($dir.name | path basename) let kcl_path = ($dir.name | path join "kcl") let kcl_mod_path = ($kcl_path | path join "kcl.mod") if ($kcl_mod_path | path exists) { extract_provider_metadata $provider_name $kcl_path } } | compact | sort-by name } # Extract metadata from a provider's KCL module def extract_provider_metadata [name: string, kcl_path: string]: nothing -> record { let kcl_mod_path = ($kcl_path | path join "kcl.mod") let mod_content = (open $kcl_mod_path | from toml) # Find KCL schema files let schema_files = (glob ($kcl_path | path join "*.k")) let main_schema = ($schema_files | where ($it | str contains $name) | first | default "") # Extract dependencies let dependencies = ($mod_content.dependencies? | default {} | columns) # Get description from schema file if available let description = if ($main_schema != "") { extract_schema_description $main_schema } else { $"($name | str title-case) cloud provider" } # Determine provider type let provider_type = match $name { "aws" => "cloud" "upcloud" => "cloud" "gcp" => "cloud" "azure" => "cloud" "local" => "local" _ => "unknown" } { name: $name type: "provider" provider_type: $provider_type version: $mod_content.package.version kcl_path: $kcl_path main_schema: $main_schema dependencies: $dependencies description: $description available: true last_updated: (ls $kcl_mod_path | get 0.modified) } } # Extract description from KCL schema file def extract_schema_description [schema_file: string]: nothing -> string { if not ($schema_file | path exists) { return "" } # Read first few lines to find description let content = (open $schema_file | lines | take 10) let description_lines = ($content | where ($it | str starts-with "# ") | take 3) if ($description_lines | is-empty) { return "" } $description_lines | str replace "^# " "" | str join " " | str trim } # Search providers by name or type export def search-providers [query: string]: nothing -> list { discover-providers | where ($it.name | str contains $query) or ($it.provider_type | str contains $query) or ($it.description | str contains $query) } # Get specific provider info export def get-provider-info [name: string]: nothing -> record { let providers = (discover-providers) let found = ($providers | where name == $name | first) if ($found | is-empty) { error make { msg: $"Provider '($name)' not found" } } $found } # List providers by type export def list-providers-by-type [type: string]: nothing -> list { discover-providers | where provider_type == $type } # Validate provider availability export def validate-providers [names: list]: nothing -> record { let available = (discover-providers | get name) let missing = ($names | where ($it not-in $available)) let found = ($names | where ($it in $available)) { requested: $names found: $found missing: $missing valid: ($missing | is-empty) } } # Get default provider (first cloud provider found) export def get-default-provider []: nothing -> string { let cloud_providers = (list-providers-by-type "cloud") if ($cloud_providers | is-empty) { "local" } else { $cloud_providers | first | get name } }