private/Resolve-WtwHost.ps1
|
$script:WtwSshConfigDir = Join-Path $HOME '.ssh' $script:WtwSshConfigPath = Join-Path $script:WtwSshConfigDir 'config' $script:WtwSshManagedRelative = 'config.d/wtw' function Split-WtwAliasList { <# .SYNOPSIS Normalise an alias argument to a clean string array. .DESCRIPTION `wtw host add x --alias at,troll` reaches us as @('at','troll') because PowerShell's argument mode already splits on commas; the same command with the value quoted arrives as the single string 'at,troll'. Accept both, and split any element that still contains commas. #> [CmdletBinding()] param([AllowNull()] [string[]] $Value) if (-not $Value) { return , @() } return , @($Value | ForEach-Object { $_ -split ',' } | ForEach-Object { $_.Trim() } | Where-Object { $_ }) } function Get-WtwHosts { <# .SYNOPSIS Remote machines declared in ~/.wtw/config.json. .DESCRIPTION Hosts live in the user config, not in a repo file. A repo-level map would duplicate what the remote's own wtw registry already knows and would go stale every time an agent creates or removes a worktree over there — which, in an agent-driven setup, is constantly. .OUTPUTS Array of @{ Name; Aliases; User; HostName; Port; IdentityFile; IdentitiesOnly; Platform; Wtw }. #> [CmdletBinding()] param([AllowNull()] [object] $Config) if ($null -eq $Config) { $Config = Get-WtwConfig } $hosts = Get-WtwPropertyValue -Object $Config -Name 'hosts' if (-not $hosts) { return , @() } $result = @() foreach ($name in (Get-WtwPropertyNames -Object $hosts)) { $entry = Get-WtwPropertyValue -Object $hosts -Name $name # `hostNames` is an ordered candidate list — an mDNS name first, a # last-known IP as backup — because a laptop on DHCP (or moving between # wifi and a USB adapter) changes address constantly while its .local # name does not. `hostName` (singular) is the pre-0.2 single-value form. $candidates = @(Get-WtwPropertyValue -Object $entry -Name 'hostNames' -DefaultValue @()) if ($candidates.Count -eq 0) { $legacy = Get-WtwPropertyValue -Object $entry -Name 'hostName' if ($legacy) { $candidates = @($legacy) } } $result += @{ Name = $name Aliases = @(Get-WtwPropertyValue -Object $entry -Name 'aliases' -DefaultValue @()) User = Get-WtwPropertyValue -Object $entry -Name 'user' HostNames = $candidates HostName = ($candidates | Select-Object -First 1) Port = Get-WtwPropertyValue -Object $entry -Name 'port' IdentityFile = Get-WtwPropertyValue -Object $entry -Name 'identityFile' IdentitiesOnly = Get-WtwPropertyValue -Object $entry -Name 'identitiesOnly' -DefaultValue $false # Drives both the remote URI path shape (drive letters) and whether # remote.SSH.remotePlatform needs pinning in the editor's settings. Platform = (Get-WtwPropertyValue -Object $entry -Name 'platform' -DefaultValue 'linux') Wtw = (Get-WtwPropertyValue -Object $entry -Name 'wtw' -DefaultValue 'wtw') # Explicit pwsh path, for installs the standard probe list misses. Pwsh = (Get-WtwPropertyValue -Object $entry -Name 'pwsh') } } # Comma operator: without it a single configured host unrolls to a bare # hashtable, and `(Get-WtwHosts)[0]` becomes a key lookup returning $null. return , @($result) } function Resolve-WtwHost { <# .SYNOPSIS Resolve a host name or alias to its configured entry. .DESCRIPTION Exact name, then exact alias, then unique prefix across both. Prefix resolution is deliberately strict: an ambiguous prefix returns $null rather than guessing, because guessing wrong here opens an editor against the wrong machine's filesystem. .PARAMETER Name Host name or alias ('arctictroll', 'at'). .PARAMETER Hosts Pre-read host list. Defaults to the configured hosts. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string] $Name, [AllowNull()] [object[]] $Hosts ) if ($null -eq $Hosts) { $Hosts = Get-WtwHosts } if ($Hosts.Count -eq 0) { return $null } $exact = $Hosts | Where-Object { $_.Name -ieq $Name } | Select-Object -First 1 if ($exact) { return $exact } $byAlias = $Hosts | Where-Object { @($_.Aliases) -contains $Name } | Select-Object -First 1 if ($byAlias) { return $byAlias } $prefixed = @($Hosts | Where-Object { $_.Name.StartsWith($Name, [System.StringComparison]::OrdinalIgnoreCase) -or (@($_.Aliases) | Where-Object { $_.StartsWith($Name, [System.StringComparison]::OrdinalIgnoreCase) }) }) if ($prefixed.Count -eq 1) { return $prefixed[0] } return $null } function Get-WtwHostNames { <# .SYNOPSIS Every name and alias that can address a configured host. .DESCRIPTION Used by the `--on`-less shorthand (`wtw at cursor auth`) and by tab completion, both of which need the flat token list rather than entries. #> [CmdletBinding()] param([AllowNull()] [object[]] $Hosts) if ($null -eq $Hosts) { $Hosts = Get-WtwHosts } $names = @() foreach ($h in $Hosts) { $names += $h.Name $names += @($h.Aliases) } return , @($names | Where-Object { $_ } | Select-Object -Unique) } |