Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 35 additions & 3 deletions .antigravity-plugin/hooks/ensure-monk-agent.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,41 @@ $ErrorActionPreference = "SilentlyContinue"
# Drain stdin so Antigravity's writer never blocks, even though we ignore it.
[Console]::In.ReadToEnd() | Out-Null

# If bash is available the .sh sibling handles this; bow out silently so the two
# hooks never both emit JSON (Antigravity runs every hook in the list).
if (Get-Command bash -ErrorAction SilentlyContinue) { exit 0 }
# If a usable bash is available the .sh sibling handles this; bow out silently
# so the two hooks never both emit JSON (Antigravity runs every hook in the
# list). Command presence alone is insufficient: Windows can expose the legacy
# WSL bash.exe launcher even when no distro contains /bin/bash, in which case
# the POSIX sibling cannot run and this native hook still needs to handle the
# invocation.
$BashUsable = $false
$BashCommand = Get-Command bash -ErrorAction SilentlyContinue
if ($BashCommand -and $BashCommand.CommandType -eq "Application") {
$BashProbe = New-Object System.Diagnostics.Process
try {
$BashProbe.StartInfo.FileName = $BashCommand.Source
$BashProbe.StartInfo.Arguments = '-lc "exit 0"'
$BashProbe.StartInfo.UseShellExecute = $false
$BashProbe.StartInfo.RedirectStandardOutput = $true
$BashProbe.StartInfo.RedirectStandardError = $true
$BashProbe.StartInfo.CreateNoWindow = $true
if ($BashProbe.Start()) {
# Drain redirected streams asynchronously so a broken launcher cannot
# block while reporting its own startup failure.
$null = $BashProbe.StandardOutput.ReadToEndAsync()
$null = $BashProbe.StandardError.ReadToEndAsync()
if ($BashProbe.WaitForExit(2000)) {
$BashUsable = $BashProbe.ExitCode -eq 0
} else {
$BashProbe.Kill()
}
}
} catch {
$BashUsable = $false
} finally {
$BashProbe.Dispose()
}
}
if ($BashUsable) { exit 0 }

$Port = if ($env:MONK_AGENT_PORT) { $env:MONK_AGENT_PORT } else { "7419" }
$AgentHost = if ($env:MONK_AGENT_HOST) { $env:MONK_AGENT_HOST } else { "127.0.0.1" }
Expand Down
105 changes: 105 additions & 0 deletions tests/antigravity-ensure-unusable-bash-stub.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
$ErrorActionPreference = "Stop"

$RepoRoot = Split-Path -Parent $PSScriptRoot
$Hook = Join-Path $RepoRoot ".antigravity-plugin\hooks\ensure-monk-agent.ps1"
$PowerShell = Join-Path $env:SystemRoot "System32\WindowsPowerShell\v1.0\powershell.exe"
$TempRoot = Join-Path ([IO.Path]::GetTempPath()) ("monk-antigravity-bash-stub-" + [Guid]::NewGuid().ToString("N"))

New-Item -ItemType Directory -Force -Path $TempRoot | Out-Null
try {
# Model the legacy Windows/WSL bash.exe launcher when WSL is installed but
# no usable /bin/bash exists. Get-Command succeeds; execution does not.
$BashStub = Join-Path $TempRoot "bash.exe"
$BashSource = Join-Path $TempRoot "fake-bash.cs"
@"
using System;
using System.Threading;
class Program {
static int Main() {
int delay;
if (int.TryParse(Environment.GetEnvironmentVariable("FAKE_BASH_DELAY_MS"), out delay) && delay > 0) {
Thread.Sleep(delay);
}
int exitCode;
return int.TryParse(Environment.GetEnvironmentVariable("FAKE_BASH_EXIT"), out exitCode) ? exitCode : 1;
}
}
"@ | Set-Content $BashSource

$Csc = Join-Path $env:WINDIR "Microsoft.NET\Framework64\v4.0.30319\csc.exe"
if (-not (Test-Path $Csc)) {
$Csc = Get-ChildItem "$env:WINDIR\Microsoft.NET\Framework64" -Filter csc.exe -Recurse |
Select-Object -First 1 -ExpandProperty FullName
}
if (-not $Csc) {
throw "csc.exe not found; cannot build the fake Bash launcher"
}
& $Csc /nologo /out:$BashStub $BashSource | Out-Null
if (-not (Test-Path $BashStub)) {
throw "failed to compile fake Bash launcher"
}

$OldPath = $env:PATH
$OldAgentPath = $env:MONK_AGENT_PATH
$OldPort = $env:MONK_AGENT_PORT
$OldBashExit = $env:FAKE_BASH_EXIT
$OldBashDelay = $env:FAKE_BASH_DELAY_MS
try {
$env:PATH = "$TempRoot;$env:SystemRoot\System32"
$env:MONK_AGENT_PATH = Join-Path $TempRoot "missing-monk-agent.exe"
$env:MONK_AGENT_PORT = "65534"
$env:FAKE_BASH_EXIT = "1"
$env:FAKE_BASH_DELAY_MS = "0"

$SelectedBash = Get-Command bash -ErrorAction Stop
if ($SelectedBash.Source -ne $BashStub) {
throw "fixture setup failed: expected $BashStub, got $($SelectedBash.Source)"
}

$Output = "{}" | & $PowerShell -NoProfile -ExecutionPolicy Bypass -File $Hook
if ($LASTEXITCODE -ne 0) {
throw "ensure hook exited with status $LASTEXITCODE"
}

$Text = [string]::Join("`n", @($Output))
if (-not $Text.Contains("monk-agent is not installed")) {
throw "FAIL: unusable bash command made the native Antigravity hook exit without its install guidance; stdout='$Text'"
}

# Preserve the intended de-duplication behavior when Bash is actually
# runnable: the native hook should yield to the concurrently registered
# POSIX sibling and emit nothing itself.
$env:FAKE_BASH_EXIT = "0"
$DelegatedOutput = "{}" | & $PowerShell -NoProfile -ExecutionPolicy Bypass -File $Hook
if ($LASTEXITCODE -ne 0) {
throw "ensure hook exited with status $LASTEXITCODE for a usable Bash command"
}
if (@($DelegatedOutput).Count -ne 0) {
throw "usable Bash command should suppress the native Antigravity hook; stdout='$DelegatedOutput'"
}

# A launcher that never returns must not consume the host's entire hook
# budget. The bounded probe should fall back to native guidance promptly.
$env:FAKE_BASH_DELAY_MS = "10000"
$Timer = [Diagnostics.Stopwatch]::StartNew()
$HungOutput = "{}" | & $PowerShell -NoProfile -ExecutionPolicy Bypass -File $Hook
$Timer.Stop()
$HungText = [string]::Join("`n", @($HungOutput))
if (-not $HungText.Contains("monk-agent is not installed")) {
throw "hung Bash command suppressed native install guidance; stdout='$HungText'"
}
if ($Timer.Elapsed.TotalSeconds -gt 6) {
throw "hung Bash liveness probe exceeded its bounded fallback time: $($Timer.Elapsed.TotalSeconds)s"
}
} finally {
$env:PATH = $OldPath
$env:MONK_AGENT_PATH = $OldAgentPath
$env:MONK_AGENT_PORT = $OldPort
$env:FAKE_BASH_EXIT = $OldBashExit
$env:FAKE_BASH_DELAY_MS = $OldBashDelay
}
} finally {
Remove-Item -Recurse -Force $TempRoot -ErrorAction SilentlyContinue
}

Write-Output "PASS: unusable bash command does not suppress the native Antigravity ensure hook"