diff options
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/Verify-PublishedHttp.ps1 | 87 | ||||
| -rw-r--r-- | scripts/Verify-Readme.ps1 | 258 |
2 files changed, 345 insertions, 0 deletions
diff --git a/scripts/Verify-PublishedHttp.ps1 b/scripts/Verify-PublishedHttp.ps1 new file mode 100644 index 0000000..a02a28b --- /dev/null +++ b/scripts/Verify-PublishedHttp.ps1 @@ -0,0 +1,87 @@ +param( + [string]$BundlePath = 'build\Release\publish\agent-sandbox-mcp-1.0.0-win-x64', + [string]$TestRoot = 'C:\tmp', + [string]$EvidenceRoot = 'artifacts\evidence\published-http-cmd-verification' +) + +$ErrorActionPreference = 'Stop' +$OutputEncoding = [Text.UTF8Encoding]::new($false) +[Console]::OutputEncoding = $OutputEncoding +$repository = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path +$bundle = [IO.Path]::GetFullPath((Join-Path $repository $BundlePath)) +$publishRoot = [IO.Path]::GetFullPath((Join-Path $repository 'build\Release\publish')) +if (-not $bundle.StartsWith($publishRoot + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) { + throw 'BundlePath must remain inside build\Release\publish.' +} +$testRootPath = (Resolve-Path -LiteralPath $TestRoot).Path +$evidence = [IO.Path]::GetFullPath((Join-Path $repository $EvidenceRoot)) +if (-not $evidence.StartsWith($repository + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) { + throw 'Evidence output must remain inside the repository.' +} +$administrator = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole( + [Security.Principal.WindowsBuiltInRole]::Administrator) +if (-not $administrator) { throw 'Published HTTP verification must run as Windows Administrator.' } + +$agent = Join-Path $bundle 'agent-mcp.exe' +$config = Join-Path $bundle 'agent.yaml' +$clientProject = Join-Path $repository 'scripts\E2EClient\E2EClient.csproj' +$runRoot = Join-Path $testRootPath ('agent-mcp-http-cmd-' + [Guid]::NewGuid().ToString('N')) +$runRootForward = $runRoot.Replace('\', '/') +$endpoint = 'http://127.0.0.1:8080/mcp' +$log = Join-Path $evidence 'verification.log' +$server = $null +New-Item -ItemType Directory -Force -Path $evidence | Out-Null +New-Item -ItemType Directory -Path $runRoot | Out-Null +$aclBefore = (Get-Acl -LiteralPath $runRoot).Sddl + +$request = @" +実作業を行ってください。作業ディレクトリは $runRootForward です。 +file_editで probe.py と run-probe.cmd を作成してください。 +probe.pyはsys.prefixがC:\tmp\.venvであることを検証し、VENV_PYTHON_OKと表示してください。 +run-probe.cmdは次を順番に実行し、どれかが失敗したら非0で終了してください。 +1. 非修飾名 cmd.exe を子Processとして実行する。 +2. 非修飾名 curl.exe --version を子Processとして実行する。 +3. curl.exeで $endpoint へ接続を試み、sandbox.network.access=noneにより失敗することを確認する。接続できた場合は失敗とする。 +4. Workspace相対path ..\.venv\Scripts\python.exe でprobe.pyを実行する。 +最後にInternal Tool process_runを executable=cmd、cwd=$runRootForward、args=["/d","/c","call run-probe.cmd"] で実行し、exit code 0とCMD_HTTP_MCP_OKを確認してください。説明だけで完了せず、必ずファイル作成とprocess_runを実行してください。 +"@ + +try { + & $agent check --config $config --lang ja 2>&1 | Tee-Object -FilePath $log + if ($LASTEXITCODE -ne 0) { throw "Published check failed with exit code $LASTEXITCODE." } + + $server = Start-Process -FilePath $agent -ArgumentList @('serve','--config',$config,'--lang','ja','--transport','http') ` + -WorkingDirectory $bundle -WindowStyle Hidden -PassThru + $ready = $false + for ($attempt = 0; $attempt -lt 100; $attempt++) { + if ($server.HasExited) { throw "Published server exited before readiness: $($server.ExitCode)" } + try { + $response = Invoke-WebRequest -Uri $endpoint -Method Get -SkipHttpErrorCheck + if ($response.StatusCode -eq 405) { $ready = $true; break } + } + catch { } + Start-Sleep -Milliseconds 100 + } + if (-not $ready) { throw 'Published HTTP MCP server did not become ready.' } + + dotnet run --project $clientProject -c Release --no-build -- http $endpoint unused $request 2>&1 | + Tee-Object -FilePath $log -Append + if ($LASTEXITCODE -ne 0) { throw "HTTP MCP agent_run failed with exit code $LASTEXITCODE." } + if (-not (Test-Path -LiteralPath (Join-Path $runRoot 'probe.py')) -or + -not (Test-Path -LiteralPath (Join-Path $runRoot 'run-probe.cmd'))) { + throw 'MCP Task did not create the required verification files.' + } +} +finally { + if ($server -and -not $server.HasExited) { $server.Kill($true); $server.WaitForExit() } + if (Test-Path -LiteralPath $runRoot) { + if ((Get-Acl -LiteralPath $runRoot).Sddl -ne $aclBefore) { throw 'MCP Task did not restore the test directory ACL.' } + $resolvedRunRoot = (Resolve-Path -LiteralPath $runRoot).Path + if (-not $resolvedRunRoot.StartsWith($testRootPath.TrimEnd('\') + '\', [StringComparison]::OrdinalIgnoreCase)) { + throw 'Refusing to clean a test directory outside TestRoot.' + } + Remove-Item -LiteralPath $resolvedRunRoot -Recurse -Force + } +} + +Write-Output "Published HTTP MCP cmd verification PASS. Evidence: $evidence" diff --git a/scripts/Verify-Readme.ps1 b/scripts/Verify-Readme.ps1 new file mode 100644 index 0000000..f67f640 --- /dev/null +++ b/scripts/Verify-Readme.ps1 @@ -0,0 +1,258 @@ +param( + [Parameter(Mandatory)] + [string]$TestRoot, + [Parameter(Mandatory)] + [string]$VenvPython, + [string]$EvidenceRoot = 'artifacts\evidence\readme-verification', + [switch]$CliOnly +) + +$ErrorActionPreference = 'Stop' +$OutputEncoding = [Text.UTF8Encoding]::new($false) +[Console]::OutputEncoding = $OutputEncoding +$repository = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path +$testRootPath = (Resolve-Path -LiteralPath $TestRoot).Path +$venvPythonPath = (Resolve-Path -LiteralPath $VenvPython).Path +$evidence = [IO.Path]::GetFullPath((Join-Path $repository $EvidenceRoot)) +if (-not $evidence.StartsWith($repository + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) { + throw 'Evidence output must remain inside the repository.' +} +$administrator = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole( + [Security.Principal.WindowsBuiltInRole]::Administrator) +if (-not $administrator -and -not $CliOnly) { throw 'README verification must run as Windows Administrator unless -CliOnly is specified.' } +if (-not (Test-Path -LiteralPath $venvPythonPath -PathType Leaf)) { throw 'VenvPython must be an existing file.' } + +$pwshPath = (Get-Command pwsh.exe -ErrorAction Stop).Source +$pwshRuntime = Split-Path $pwshPath +$cmdPath = $env:ComSpec +$venvRoot = Split-Path (Split-Path $venvPythonPath) +$pyvenv = Get-Content -LiteralPath (Join-Path $venvRoot 'pyvenv.cfg') +$pythonRuntime = (($pyvenv | Where-Object { $_ -match '^home\s*=' }) -split '=', 2)[1].Trim() +if (-not (Test-Path -LiteralPath $pythonRuntime -PathType Container)) { throw "Python Runtime Root does not exist: $pythonRuntime" } +$basePythonPath = Join-Path $pythonRuntime 'python.exe' +if (-not (Test-Path -LiteralPath $basePythonPath -PathType Leaf)) { throw "Base Python does not exist: $basePythonPath" } + +New-Item -ItemType Directory -Force -Path $evidence | Out-Null +$runRoot = Join-Path $testRootPath ('agent-mcp-readme-cli-' + [Guid]::NewGuid().ToString('N')) +New-Item -ItemType Directory -Path $runRoot | Out-Null +$agent = Join-Path $repository 'build\Release\Agent.App\net10.0-windows10.0.26100.0\agent-mcp.exe' +$windowsTests = Join-Path $repository 'build\Release\Agent.Windows.Tests\net10.0-windows10.0.26100.0\Agent.Windows.Tests.exe' +$configPath = Join-Path $runRoot 'agent.yaml' +$logPath = Join-Path $evidence 'verification.log' +$failed = $false +$aliases = @( + ('readme-python-' + [Guid]::NewGuid().ToString('N').Substring(0, 8)) + ('readme-powershell-' + [Guid]::NewGuid().ToString('N').Substring(0, 8)) + ('readme-cmd-' + [Guid]::NewGuid().ToString('N').Substring(0, 8)) +) +$pythonAlias, $powershellAlias, $cmdAlias = $aliases +$listener = [Net.Sockets.TcpListener]::new([Net.IPAddress]::Loopback, 0) +$listener.Start() +$httpPort = ([Net.IPEndPoint]$listener.LocalEndpoint).Port +$listener.Stop() +$resolutionStorePath = $null +$server = $null + +function Invoke-Step { + param([string]$Name, [scriptblock]$Action) + "START $Name $(Get-Date -Format o)" | Tee-Object -FilePath $logPath -Append + try { + $global:LASTEXITCODE = 0 + & $Action *>&1 | Tee-Object -FilePath $logPath -Append + if ($LASTEXITCODE -ne 0) { throw "$Name exited with $LASTEXITCODE" } + "PASS $Name $(Get-Date -Format o)" | Tee-Object -FilePath $logPath -Append + } + catch { + $_ | Out-String | Tee-Object -FilePath $logPath -Append + $script:failed = $true + "FAIL $Name $(Get-Date -Format o)" | Tee-Object -FilePath $logPath -Append + } +} + +function Invoke-ExpectedFailure { + param([string]$Name, [string[]]$Arguments) + "START $Name $(Get-Date -Format o)" | Tee-Object -FilePath $logPath -Append + & $agent @Arguments *>&1 | Tee-Object -FilePath $logPath -Append + if ($LASTEXITCODE -ne 2) { + $script:failed = $true + "FAIL $Name expected_exit=2 actual_exit=$LASTEXITCODE" | Tee-Object -FilePath $logPath -Append + } + else { "PASS $Name expected_exit=2" | Tee-Object -FilePath $logPath -Append } +} + +function Start-And-ProbeServer { + param([bool]$ExplicitServe) + $arguments = if ($ExplicitServe) { + @('serve', '--config', $configPath, '--lang', 'ja', '--transport', 'http') + } else { + @('--config', $configPath, '--lang', 'ja', '--transport', 'http') + } + $script:server = Start-Process -FilePath $agent -ArgumentList $arguments -WorkingDirectory (Split-Path $agent) -WindowStyle Hidden -PassThru + $endpoint = "http://127.0.0.1:$httpPort/mcp" + for ($attempt = 0; $attempt -lt 100; $attempt++) { + if ($script:server.HasExited) { throw "Server exited before becoming ready: $($script:server.ExitCode)" } + try { + $response = Invoke-WebRequest -Uri $endpoint -Method Get -SkipHttpErrorCheck + if ($response.StatusCode -eq 405) { return } + } catch { } + Start-Sleep -Milliseconds 100 + } + throw 'Server did not become ready.' +} + +try { + $forwardRoot = $testRootPath.Replace('\', '/') + $forwardPrompt = (Join-Path $repository 'prompts\user-system.md').Replace('\', '/') + $forwardSkills = (Join-Path $repository 'skills').Replace('\', '/') + $forwardLocales = (Join-Path $repository 'config\locales').Replace('\', '/') + $forwardLogs = (Join-Path $runRoot 'logs').Replace('\', '/') + @" +schema_version: 1 +application: + language: en +mcp: + transport: http + http: + host: 127.0.0.1 + port: $httpPort + images: + max_count: 4 + max_bytes_each: 10485760 + max_total_bytes: 20971520 + tasks: + time_to_live_hours: 24 + poll_interval_ms: 1000 +agent: + user_system_prompt: $forwardPrompt + max_iterations: 8 + same_failure_limit: 2 +ollama: + endpoint: http://127.0.0.1:11434 + model: deepseek-v4-flash:cloud + think: true + keep_alive: 5m + options: + num_ctx: 32768 + temperature: 0 + extra_options: {} +planner: + max_tasks: 8 +sandbox: + profile: development + workspaces: + - $forwardRoot + mounts: [] + execution: + executables: + - $pythonAlias + shells: + - $powershellAlias + - $cmdAlias + network: + access: none + local_endpoints: [] + restricted_endpoints: [] + resources: + profile: constrained +file_edit: + memory_backup_limit_mb: 16 +limits: + whole_read_bytes: 1048576 + search_max_results: 1000 + tail_max_lines: 10000 + context_summary_ratio: 0.8 +localization: + directory: $forwardLocales + fallback: en +skills: + directory: $forwardSkills +logging: + directory: $forwardLogs + level: information + http_requests: true + max_file_bytes: 10485760 + retained_files: 5 +"@ | Set-Content -LiteralPath $configPath -Encoding utf8NoBOM + + @( + "timestamp=$(Get-Date -Format o)", + "commit=$(git -C $repository rev-parse HEAD)", + "administrator=$administrator", + "os=$([Environment]::OSVersion.VersionString)", + "venv_python=$venvPythonPath", + "venv_version=$(& $venvPythonPath --version)", + "powershell=$pwshPath", + "cmd=$cmdPath" + ) | Set-Content -LiteralPath (Join-Path $evidence 'metadata.txt') -Encoding utf8NoBOM + + Invoke-Step 'cli-sandbox-resolve' { & $agent sandbox resolve --config $configPath --lang ja } + Invoke-Step 'cli-sandbox-confirm-yes' { & $agent sandbox confirm --yes --config $configPath --lang ja } + $normalizedConfig = [IO.Path]::GetFullPath($configPath).ToUpperInvariant() + $hash = [Convert]::ToHexString([Security.Cryptography.SHA256]::HashData([Text.Encoding]::UTF8.GetBytes($normalizedConfig))) + $resolutionStorePath = Join-Path $env:LOCALAPPDATA "AgentSandboxMcp\environment-resolutions\$hash.json" + Invoke-Step 'cli-executable-resolve-path' { & $agent executable resolve $pythonAlias --path $basePythonPath --config $configPath --lang ja } + Invoke-Step 'cli-executable-approve-runtime-root-yes' { & $agent executable approve $pythonAlias --path $basePythonPath --runtime-root $pythonRuntime --yes --config $configPath --lang ja } + Invoke-Step 'cli-shell-resolve-powershell' { & $agent executable resolve $powershellAlias --path $pwshPath --config $configPath --lang en } + Invoke-Step 'cli-shell-approve-runtime-root-yes' { & $agent executable approve $powershellAlias --path $pwshPath --runtime-root $pwshRuntime --yes --config $configPath --lang en } + Invoke-Step 'cli-shell-resolve-cmd' { & $agent executable resolve $cmdAlias --path $cmdPath --config $configPath --lang en } + Invoke-Step 'cli-shell-approve-cmd-no-runtime' { & $agent executable approve $cmdAlias --path $cmdPath --no-runtime --yes --config $configPath --lang en } + Invoke-Step 'cli-executable-list' { & $agent executable list --config $configPath --lang ja } + Invoke-Step 'cli-sandbox-show-text-default-format' { & $agent sandbox show --config $configPath --lang en } + Invoke-Step 'cli-sandbox-show-json' { & $agent sandbox show --format json --config $configPath --lang ja } + if ($CliOnly) { + Invoke-ExpectedFailure 'cli-check-detects-non-admin' @('check', '--config', $configPath, '--lang', 'ja') + } + else { + Invoke-Step 'cli-check' { & $agent check --config $configPath --lang ja } + } + + Invoke-ExpectedFailure 'cli-invalid-transport' @('serve', '--config', $configPath, '--transport', 'invalid') + Invoke-ExpectedFailure 'cli-invalid-format' @('sandbox', 'show', '--format', 'xml', '--config', $configPath) + Invoke-ExpectedFailure 'cli-management-missing-config' @('sandbox', 'resolve') + Invoke-ExpectedFailure 'cli-mutually-exclusive-runtime-options' @('executable', 'approve', $pythonAlias, '--path', $basePythonPath, '--runtime-root', $pythonRuntime, '--no-runtime', '--config', $configPath) + + if (-not $CliOnly) { + Invoke-Step 'cli-serve-explicit-http' { + Start-And-ProbeServer $true + $script:server.Kill($true) + $script:server.WaitForExit() + $script:server = $null + } + Invoke-Step 'cli-serve-implicit-option-first' { + Start-And-ProbeServer $false + $script:server.Kill($true) + $script:server.WaitForExit() + $script:server = $null + } + + $env:AGENT_MCP_MONKEY_ROOT = $testRootPath + $env:AGENT_MCP_TEST_VENV_PYTHON = $venvPythonPath + $env:AGENT_MCP_TEST_POWERSHELL = $pwshPath + $env:AGENT_MCP_TEST_POWERSHELL_RUNTIME = $pwshRuntime + $env:AGENT_MCP_TEST_CMD = $cmdPath + Invoke-Step 'readme-operational-windows-tests' { + & $windowsTests -class Agent.Windows.Tests.ReadmeOperationalTests -parallel none -failSkips + } + } + Invoke-Step 'cli-executable-revoke-python' { & $agent executable revoke $pythonAlias --yes --config $configPath --lang ja } + Invoke-Step 'cli-executable-revoke-powershell' { & $agent executable revoke $powershellAlias --yes --config $configPath --lang en } + Invoke-Step 'cli-executable-revoke-cmd' { & $agent executable revoke $cmdAlias --yes --config $configPath --lang en } +} +finally { + if ($server -and -not $server.HasExited) { $server.Kill($true); $server.WaitForExit() } + foreach ($alias in $aliases) { + & $agent executable revoke $alias --yes --config $configPath --lang en 2>$null | Out-Null + } + if ($resolutionStorePath -and (Test-Path -LiteralPath $resolutionStorePath)) { Remove-Item -LiteralPath $resolutionStorePath -Force } + if (Test-Path -LiteralPath $runRoot) { + $resolvedRunRoot = (Resolve-Path -LiteralPath $runRoot).Path + if (-not $resolvedRunRoot.StartsWith($testRootPath.TrimEnd('\') + '\', [StringComparison]::OrdinalIgnoreCase)) { + throw 'Refusing to clean a test directory outside TestRoot.' + } + Remove-Item -LiteralPath $resolvedRunRoot -Recurse -Force + } + Remove-Item Env:AGENT_MCP_MONKEY_ROOT,Env:AGENT_MCP_TEST_VENV_PYTHON,Env:AGENT_MCP_TEST_POWERSHELL,Env:AGENT_MCP_TEST_POWERSHELL_RUNTIME,Env:AGENT_MCP_TEST_CMD -ErrorAction SilentlyContinue +} + +if ($failed) { throw "README verification failed. Evidence: $evidence" } +Write-Output "README verification PASS. Evidence: $evidence" |
