param( [Parameter(Mandatory = $true)] [string]$WorkerName, [Parameter(Mandatory = $true)] [string]$WorkerType, [string]$DataRoot = (Join-Path ([Environment]::GetFolderPath('LocalApplicationData')) 'UnifiedWorkers'), [switch]$CommandOnly, [string]$Command ) $ErrorActionPreference = 'Stop' function Get-WorkerPaths { param([string]$Root, [string]$Type, [string]$Name) $instanceRoot = Join-Path -Path (Join-Path -Path $Root -ChildPath $Type) -ChildPath $Name return [pscustomobject]@{ Metadata = Join-Path -Path $instanceRoot -ChildPath 'state\worker-info.json' Command = Join-Path -Path $instanceRoot -ChildPath 'state\commands.txt' Log = Join-Path -Path $instanceRoot -ChildPath 'logs\worker.log' } } $paths = Get-WorkerPaths -Root $DataRoot -Type $WorkerType -Name $WorkerName if (-not (Test-Path $paths.Metadata)) { Write-Host "No worker metadata found for $WorkerName ($WorkerType)." -ForegroundColor Red exit 1 } try { $metadata = Get-Content -Path $paths.Metadata -Raw | ConvertFrom-Json } catch { Write-Host "Unable to read worker metadata: $($_.Exception.Message)" -ForegroundColor Red exit 1 } if (Test-Path $paths.Log) { # ensure log file exists but do not truncate $null = (Get-Item $paths.Log) } else { New-Item -Path $paths.Log -ItemType File -Force | Out-Null } if (-not (Test-Path $paths.Command)) { New-Item -Path $paths.Command -ItemType File -Force | Out-Null } function Send-WorkerCommand { param([string]$Value) $clean = $Value.Trim() if (-not $clean) { return } Add-Content -Path $paths.Command -Value $clean -Encoding UTF8 Write-Host "Sent command '$clean' to $WorkerName." -ForegroundColor DarkGray } if ($CommandOnly) { if (-not $Command) { Write-Host "CommandOnly flag set but no command provided." -ForegroundColor Red exit 1 } Send-WorkerCommand -Value $Command exit 0 } Write-Host "Attaching to $WorkerName ($WorkerType) logs." -ForegroundColor Cyan Write-Host "Type commands and press Enter. Type 'detach' to exit session." -ForegroundColor Yellow $logJob = Start-Job -ScriptBlock { param($LogPath) Get-Content -Path $LogPath -Tail 50 -Wait } -ArgumentList $paths.Log try { while ($true) { $input = Read-Host "> " if ($null -eq $input) { continue } $normalized = $input.Trim() if ($normalized.StartsWith(':')) { $normalized = $normalized.TrimStart(':').Trim() } if ($normalized.Length -eq 0) { continue } if ($normalized.ToLowerInvariant() -eq 'detach') { break } Send-WorkerCommand -Value $input } } finally { if ($logJob) { Stop-Job -Job $logJob -ErrorAction SilentlyContinue Receive-Job -Job $logJob -ErrorAction SilentlyContinue | Out-Null Remove-Job -Job $logJob -ErrorAction SilentlyContinue } Write-Host "Detached from worker $WorkerName." -ForegroundColor Cyan }