# A2A node manager. Usage: manage.ps1 install|start|stop|status|uninstall [-Token ] [-Port ] param([Parameter(Mandatory)][ValidateSet('install','start','stop','status','uninstall')]$Action, [string]$Token, [int]$Port = 0) $ErrorActionPreference = 'Stop' $a2aHome = Join-Path $env:USERPROFILE '.a2a' $dst = Join-Path $a2aHome 'node' $src = Join-Path $PSScriptRoot 'a2a.py' $task = 'A2A_node' $py = (Get-Command pythonw.exe -ErrorAction SilentlyContinue).Source if (-not $py) { $py = (Get-Command python.exe).Source } $script = Join-Path $dst 'a2a.py' function Stop-Node { Get-CimInstance Win32_Process | Where-Object { $_.Name -like 'python*' -and $_.CommandLine -like '*a2a.py*serve*' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force } } function Start-Node { Stop-Node Start-Process -FilePath $py -ArgumentList "`"$script`" serve" -WindowStyle Hidden } # Best-effort inbound firewall rule (needs admin; never triggers UAC). function Ensure-Firewall { $fwPort = 7788 try { $c = Get-Content (Join-Path $a2aHome 'config.json') -Raw | ConvertFrom-Json; if ($c.port) { $fwPort = [int]$c.port } } catch {} if ($Port) { $fwPort = $Port } $name = 'A2A_node' $cmd = "New-NetFirewallRule -DisplayName $name -Direction Inbound -Action Allow -Protocol TCP -LocalPort $fwPort -Profile Any" try { if (Get-NetFirewallRule -DisplayName $name -ErrorAction SilentlyContinue) { "firewall: rule '$name' already exists"; return } } catch {} $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isAdmin) { "firewall: not running as administrator, rule not created. Run this in an elevated PowerShell:" " $cmd" return } try { Invoke-Expression $cmd | Out-Null; "firewall: created rule '$name' (TCP $fwPort, all profiles)" } catch { "firewall: failed to create rule: $_`n run manually (admin): $cmd" } } switch ($Action) { 'install' { New-Item -ItemType Directory -Force $dst | Out-Null Copy-Item $src $script -Force $args = @('init'); if ($Token) { $args += @('--token', $Token) }; if ($Port) { $args += @('--port', $Port) } & python.exe $script @args # Claude Code channel bridge (push wake-up) + launcher $ch = Join-Path $a2aHome 'channel' New-Item -ItemType Directory -Force $ch | Out-Null Copy-Item (Join-Path $PSScriptRoot 'a2a-channel.mjs') $ch -Force Copy-Item (Join-Path $PSScriptRoot 'channel-package.json') (Join-Path $ch 'package.json') -Force Copy-Item (Join-Path $PSScriptRoot 'channel-mcp.json') (Join-Path $ch 'mcp.json') -Force Copy-Item (Join-Path $PSScriptRoot 'claude-a2a.cmd') $dst -Force $mcp = (Get-Content (Join-Path $ch 'mcp.json') -Raw) -replace 'C:/Users/bowei', $env:USERPROFILE.Replace('\', '/') Set-Content (Join-Path $ch 'mcp.json') $mcp -Encoding ascii # via cmd: under PS 5.1 + ErrorActionPreference=Stop, npm.ps1's stderr "npm notice" lines abort the install Push-Location $ch; cmd /c "npm install --no-audit --no-fund >nul 2>&1"; $npmRc = $LASTEXITCODE; Pop-Location if ($npmRc -ne 0) { throw "npm install failed in $ch (exit $npmRc)" } # cmd shim so sessions can just call `a2a` "@python `"$script`" %*" | Set-Content (Join-Path $dst 'a2a.cmd') -Encoding ascii $act = New-ScheduledTaskAction -Execute $py -Argument "`"$script`" serve" $trg = New-ScheduledTaskTrigger -AtLogOn -User $env:USERNAME $set = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1) -ExecutionTimeLimit ([TimeSpan]::Zero) # AtLogOn tasks need admin on some machines; fall back to the per-user Run key (no admin, no auto-restart) try { Register-ScheduledTask -TaskName $task -Action $act -Trigger $trg -Settings $set -Force -ErrorAction Stop | Out-Null; "autostart: scheduled task '$task'" } catch { Set-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run' -Name $task -Value "`"$py`" `"$script`" serve" "autostart: scheduled task denied ($($_.Exception.Message.Trim())); registered HKCU Run '$task' instead" } Ensure-Firewall Start-Node Start-Sleep 2 & python.exe $script status } 'start' { Start-Node; Start-Sleep 2; & python.exe $script status } 'stop' { Stop-Node; 'stopped' } 'status' { & python.exe $script status; & python.exe $script ls; & python.exe $script peers } 'uninstall' { Stop-Node; Unregister-ScheduledTask -TaskName $task -Confirm:$false -ErrorAction SilentlyContinue Remove-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run' -Name $task -ErrorAction SilentlyContinue; 'removed (config kept in ~/.a2a)' } }