Add XR interactive desktop bridge proof
This commit is contained in:
parent
eff75c3cfa
commit
da911b07fb
10 changed files with 454 additions and 5 deletions
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"reportVersion": "ht-interactive-desktop-task/v1",
|
||||
"generatedAtUtc": "2026-07-02T07:55:45.0811556Z",
|
||||
"taskName": "HyperTwist-InteractiveDesktop-20260702-095545",
|
||||
"taskRunnerPath": "C:\\Windows\\Temp\\HyperTwist-InteractiveDesktop-20260702-095545.runner.ps1",
|
||||
"taskResultPath": "C:\\Windows\\Temp\\HyperTwist-InteractiveDesktop-20260702-095545.result.json",
|
||||
"timeoutSeconds": 210,
|
||||
"currentSessionId": 0,
|
||||
"explorerSessionIds": [
|
||||
1
|
||||
],
|
||||
"createExitCode": 0,
|
||||
"runExitCode": 0,
|
||||
"taskOutput": {
|
||||
"succeeded": true,
|
||||
"user": "DESKTOP-KS3VGHU\\Anthracite Ace",
|
||||
"processId": 15760,
|
||||
"sessionId": 1,
|
||||
"explorerSessionIds": [
|
||||
1
|
||||
],
|
||||
"generatedAtUtc": "2026-07-02T07:55:55.5084184Z",
|
||||
"outputLines": [
|
||||
"XR_WINDOWED_EXPECTED_ERROR=Windowed XR validation requires an installed active OpenXR runtime, but no OpenXR ActiveRuntime registry entry was found. Activate SteamVR, Oculus, Windows Mixed Reality, or another local OpenXR runtime on the interactive desktop and rerun.",
|
||||
"{\r\n \"reportVersion\": \"ht-xr-windowed-live-observation/v1\",\r\n \"generatedAtUtc\": \"2026-07-02T07:55:55.6890920Z\",\r\n \"packageRoot\": \"C:\\\\HyperTwist_worktrees\\\\phase10validate_packaged_xr_20260702_rebuild\",\r\n \"smokeMaps\": [\r\n \"/Game/HyperTwistTraining/Maps/L_HyperTwist_FollowAlongTraining\",\r\n \"/Game/HyperTwistTraining/Maps/L_HyperTwist_ClassicTraining\"\r\n ],\r\n \"observationSeconds\": 120,\r\n \"timeoutSeconds\": 150,\r\n \"runtimeLaunchMode\": \"windowed\",\r\n \"resolution\": {\r\n \"width\": 1600,\r\n \"height\": 900\r\n },\r\n \"smokeReports\": [\r\n {\r\n \"reportVersion\": \"ht-xr-package-smoke/v1\",\r\n \"generatedAtUtc\": \"2026-07-02T07:55:55.8312741Z\",\r\n \"packageRoot\": \"C:\\\\HyperTwist_worktrees\\\\phase10validate_packaged_xr_20260702_rebuild\",\r\n \"executablePath\": \"C:\\\\HyperTwist_worktrees\\\\phase10validate_packaged_xr_20260702_rebuild\\\\Windows\\\\UnrealHyperTwist.exe\",\r\n \"mapAssetPath\": \"/Game/HyperTwistTraining/Maps/L_HyperTwist_FollowAlongTraining\",\r\n \"mapUrl\": \"/Game/HyperTwistTraining/Maps/L_HyperTwist_FollowAlongTraining?game=/Script/UnrealHyperTwist.HyperTwistXrTrainingGameMode\",\r\n \"runtimeLaunchMode\": \"windowed\",\r\n \"observationSeconds\": 120,\r\n \"timeoutSeconds\": 150,\r\n \"resolution\": {\r\n \"width\": 1600,\r\n \"height\": 900\r\n },\r\n \"keepRunning\": false,\r\n \"runtimeReportPath\": \"C:\\\\HyperTwist_worktrees\\\\phase10validate_packaged_xr_20260702_rebuild\\\\validation\\\\windowed-live-interactive-probe\\\\_Game_HyperTwistTraining_Maps_L_HyperTwist_FollowAlongTraining.runtime.json\",\r\n \"preflight\": {\r\n \"currentSessionId\": 1,\r\n \"explorerSessionIds\": [\r\n 1\r\n ],\r\n \"isInteractiveDesktopSession\": true,\r\n \"openXrActiveRuntimePath\": null,\r\n \"openXrActiveRuntimePresent\": false\r\n },\r\n \"result\": \"failed\",\r\n \"processId\": null,\r\n \"processStopped\": false,\r\n \"exitCode\": null,\r\n \"runtimeReport\": null,\r\n \"error\": \"Windowed XR validation requires an installed active OpenXR runtime, but no OpenXR ActiveRuntime registry entry was found. Activate SteamVR, Oculus, Windows Mixed Reality, or another local OpenXR runtime on the interactive desktop and rerun.\"\r\n }\r\n ],\r\n \"preflight\": {\r\n \"currentSessionId\": 1,\r\n \"explorerSessionIds\": [\r\n 1\r\n ],\r\n \"isInteractiveDesktopSession\": true,\r\n \"openXrActiveRuntimePath\": null,\r\n \"openXrActiveRuntimePresent\": false\r\n },\r\n \"packagedHeadMountedDisplaySessionObserved\": false,\r\n \"packagedControllerInputObserved\": false,\r\n \"remainingGateIds\": [\r\n\r\n ],\r\n \"result\": \"failed\",\r\n \"error\": \"Windowed XR validation requires an installed active OpenXR runtime, but no OpenXR ActiveRuntime registry entry was found. Activate SteamVR, Oculus, Windows Mixed Reality, or another local OpenXR runtime on the interactive desktop and rerun.\"\r\n}"
|
||||
],
|
||||
"error": null
|
||||
},
|
||||
"result": "passed",
|
||||
"error": null
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
"reportVersion": "ht-xr-windowed-live-observation/v1",
|
||||
"generatedAtUtc": "2026-07-02T07:55:55.6890920Z",
|
||||
"packageRoot": "C:\\HyperTwist_worktrees\\phase10validate_packaged_xr_20260702_rebuild",
|
||||
"smokeMaps": [
|
||||
"/Game/HyperTwistTraining/Maps/L_HyperTwist_FollowAlongTraining",
|
||||
"/Game/HyperTwistTraining/Maps/L_HyperTwist_ClassicTraining"
|
||||
],
|
||||
"observationSeconds": 120,
|
||||
"timeoutSeconds": 150,
|
||||
"runtimeLaunchMode": "windowed",
|
||||
"resolution": {
|
||||
"width": 1600,
|
||||
"height": 900
|
||||
},
|
||||
"smokeReports": [
|
||||
{
|
||||
"reportVersion": "ht-xr-package-smoke/v1",
|
||||
"generatedAtUtc": "2026-07-02T07:55:55.8312741Z",
|
||||
"packageRoot": "C:\\HyperTwist_worktrees\\phase10validate_packaged_xr_20260702_rebuild",
|
||||
"executablePath": "C:\\HyperTwist_worktrees\\phase10validate_packaged_xr_20260702_rebuild\\Windows\\UnrealHyperTwist.exe",
|
||||
"mapAssetPath": "/Game/HyperTwistTraining/Maps/L_HyperTwist_FollowAlongTraining",
|
||||
"mapUrl": "/Game/HyperTwistTraining/Maps/L_HyperTwist_FollowAlongTraining?game=/Script/UnrealHyperTwist.HyperTwistXrTrainingGameMode",
|
||||
"runtimeLaunchMode": "windowed",
|
||||
"observationSeconds": 120,
|
||||
"timeoutSeconds": 150,
|
||||
"resolution": {
|
||||
"width": 1600,
|
||||
"height": 900
|
||||
},
|
||||
"keepRunning": false,
|
||||
"runtimeReportPath": "C:\\HyperTwist_worktrees\\phase10validate_packaged_xr_20260702_rebuild\\validation\\windowed-live-interactive-probe\\_Game_HyperTwistTraining_Maps_L_HyperTwist_FollowAlongTraining.runtime.json",
|
||||
"preflight": {
|
||||
"currentSessionId": 1,
|
||||
"explorerSessionIds": [
|
||||
1
|
||||
],
|
||||
"isInteractiveDesktopSession": true,
|
||||
"openXrActiveRuntimePath": null,
|
||||
"openXrActiveRuntimePresent": false
|
||||
},
|
||||
"result": "failed",
|
||||
"processId": null,
|
||||
"processStopped": false,
|
||||
"exitCode": null,
|
||||
"runtimeReport": null,
|
||||
"error": "Windowed XR validation requires an installed active OpenXR runtime, but no OpenXR ActiveRuntime registry entry was found. Activate SteamVR, Oculus, Windows Mixed Reality, or another local OpenXR runtime on the interactive desktop and rerun."
|
||||
}
|
||||
],
|
||||
"preflight": {
|
||||
"currentSessionId": 1,
|
||||
"explorerSessionIds": [
|
||||
1
|
||||
],
|
||||
"isInteractiveDesktopSession": true,
|
||||
"openXrActiveRuntimePath": null,
|
||||
"openXrActiveRuntimePresent": false
|
||||
},
|
||||
"packagedHeadMountedDisplaySessionObserved": false,
|
||||
"packagedControllerInputObserved": false,
|
||||
"remainingGateIds": [
|
||||
|
||||
],
|
||||
"result": "failed",
|
||||
"error": "Windowed XR validation requires an installed active OpenXR runtime, but no OpenXR ActiveRuntime registry entry was found. Activate SteamVR, Oculus, Windows Mixed Reality, or another local OpenXR runtime on the interactive desktop and rerun."
|
||||
}
|
||||
|
|
@ -243,3 +243,15 @@ widening.
|
|||
1. activate a real local `OpenXR` runtime on the Windows desktop
|
||||
2. run the first-party windowed observation wrapper from that interactive
|
||||
desktop session rather than through the reverse-SSH/OpenSSH session
|
||||
- the next same-day bridge continuation then tightened that host reading again:
|
||||
- the repo now also carries
|
||||
`scripts\Invoke-HyperTwistInteractiveDesktopTask.ps1`
|
||||
- checked-in bridge proof
|
||||
`docs/generated/xr/xr_interactive_desktop_task_probe_2026-07-02.json`
|
||||
now proves the reverse-SSH Session `0` lane can launch a bounded first-
|
||||
party task into the real Explorer desktop Session `1`
|
||||
- the XR lane then reran through that bridge and checked in
|
||||
`docs/generated/xr/xr_windowed_interactive_desktop_probe_2026-07-02.json`
|
||||
- that rerun shows the interactive-desktop requirement is now satisfied and
|
||||
the remaining blocker has narrowed further to one host fact only:
|
||||
`OpenXR` still has no active runtime registered on the desktop
|
||||
|
|
|
|||
|
|
@ -750,14 +750,44 @@ Operational rules reinforced by this proof:
|
|||
- the reverse-SSH lane is **not** the authoritative live windowed XR proof
|
||||
lane while it is entering Session `0` rather than the interactive desktop
|
||||
Session `1`
|
||||
- the remaining headset/controller observation gate must be run locally on
|
||||
the logged-in Windows desktop after an `OpenXR` runtime is actually active
|
||||
- canonical local Windows desktop command for that remaining gate:
|
||||
- that left the remaining headset/controller observation gate temporarily
|
||||
blocked on the interactive-desktop launch context
|
||||
- the next same-day continuation then closed that launch-context gap without
|
||||
widening beyond the XR proof lane:
|
||||
- the repo now also carries
|
||||
`scripts\Invoke-HyperTwistInteractiveDesktopTask.ps1`
|
||||
- checked-in bridge proof:
|
||||
`docs/generated/xr/xr_interactive_desktop_task_probe_2026-07-02.json`
|
||||
- that bridge proof shows:
|
||||
- scheduler task creation from Session `0`: passed
|
||||
- scheduler task run from Session `0`: passed
|
||||
- resulting task session id: `1`
|
||||
- resulting task explorer-session parity: `1`
|
||||
- a later same-day XR rerun through that new bridge then produced:
|
||||
`docs/generated/xr/xr_windowed_interactive_desktop_probe_2026-07-02.json`
|
||||
with:
|
||||
- `currentSessionId: 1`
|
||||
- `isInteractiveDesktopSession: true`
|
||||
- `openXrActiveRuntimePresent: false`
|
||||
- error narrowed to missing active `OpenXR` runtime
|
||||
- operational reading after the bridge continuation:
|
||||
- the reverse-SSH lane plus first-party interactive-desktop task bridge can
|
||||
now initiate the real Session `1` windowed XR proof path
|
||||
- the remaining blocker is no longer the desktop-session bridge
|
||||
- the remaining blocker is the absent active `OpenXR` runtime on the logged-
|
||||
in Windows desktop
|
||||
- canonical local Windows desktop command for that remaining gate is still:
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File C:\HyperTwist_worktrees\phase10validate\scripts\Invoke-HyperTwistXrWindowedObservation.ps1 -PackageRoot C:\HyperTwist_worktrees\phase10validate_packaged_xr_20260702_rebuild
|
||||
```
|
||||
|
||||
- canonical Session `0` bridge-assisted command shape is now:
|
||||
|
||||
```powershell
|
||||
& 'C:\HyperTwist_worktrees\phase10validate\scripts\Invoke-HyperTwistInteractiveDesktopTask.ps1' -InlineCommand "& 'C:\HyperTwist_worktrees\phase10validate\scripts\Invoke-HyperTwistXrWindowedObservation.ps1' -PackageRoot 'C:\HyperTwist_worktrees\phase10validate_packaged_xr_20260702_rebuild'"
|
||||
```
|
||||
|
||||
- that local desktop run writes the aggregate live-observation report beneath:
|
||||
`C:\HyperTwist_worktrees\phase10validate_packaged_xr_20260702_rebuild\validation\windowed-live\`
|
||||
|
||||
|
|
|
|||
|
|
@ -631,3 +631,17 @@ But the mature path is:
|
|||
- skills second
|
||||
- clean-room command specs for restrictive lanes
|
||||
- no donor command surface promoted into product truth by accident
|
||||
|
||||
## Update - 2026-07-02 - reverse-SSH interactive desktop bridge wrapper
|
||||
|
||||
- the current repo-local wrapper family now also includes a bounded first-party
|
||||
Windows-side interactive-desktop bridge command surface:
|
||||
`scripts\Invoke-HyperTwistInteractiveDesktopTask.ps1`
|
||||
- this wrapper does not create a new product feature by itself
|
||||
- it exists so a Session `0` reverse-SSH operator lane can trigger a bounded
|
||||
first-party task inside the real logged-in Windows desktop Session `1`,
|
||||
collect structured JSON output, and keep the bridge truth source-controlled
|
||||
- current checked-in proof for that wrapper is:
|
||||
`docs/generated/xr/xr_interactive_desktop_task_probe_2026-07-02.json`
|
||||
- the wrapper therefore belongs to the bounded workflow/review or provider/ops
|
||||
command-surface family, not to the shippable gameplay feature layer
|
||||
|
|
|
|||
|
|
@ -411,6 +411,17 @@ scripts/run-hypertwist-remote-unreal-automation-sequence.sh \
|
|||
`scripts\Invoke-HyperTwistXrWindowedObservation.ps1` exists specifically so
|
||||
that gate can fail fast with preflight evidence when the launcher is not on
|
||||
the interactive desktop or when no `OpenXR` active runtime is present
|
||||
- later same-day `2026-07-02` continuation proof then added
|
||||
`scripts\Invoke-HyperTwistInteractiveDesktopTask.ps1`, which lets the
|
||||
reverse-SSH Session `0` lane trigger a bounded first-party task inside the
|
||||
logged-in Explorer desktop Session `1`; checked-in proof now lives at
|
||||
`docs/generated/xr/xr_interactive_desktop_task_probe_2026-07-02.json`
|
||||
- after that bridge landed, the remaining live packaged XR blocker narrowed
|
||||
again: current checked-in rerun
|
||||
`docs/generated/xr/xr_windowed_interactive_desktop_probe_2026-07-02.json`
|
||||
proves the session-context requirement is satisfied and that the only
|
||||
remaining blocker is an absent active `OpenXR` runtime on the Windows
|
||||
desktop
|
||||
- for reverse-SSH/headless classic-cube replay-export validation, use
|
||||
`-NullRHI` only for the asset-authoring leg and `-RenderOffScreen` for the
|
||||
actual `MovieRenderQueue` render leg; they solve different failure modes
|
||||
|
|
|
|||
|
|
@ -418,6 +418,18 @@ truthfully claim:
|
|||
interactive Explorer desktop is Session `1`
|
||||
- that same probe also keeps the local runtime blocker explicit by recording
|
||||
that no `OpenXR` active runtime is currently registered on the host
|
||||
- a later same-day bridge continuation then removed the session-context part of
|
||||
that blocker:
|
||||
- the repo now also carries
|
||||
`scripts\Invoke-HyperTwistInteractiveDesktopTask.ps1`
|
||||
- checked-in bridge proof:
|
||||
`docs/generated/xr/xr_interactive_desktop_task_probe_2026-07-02.json`
|
||||
- checked-in bridged XR rerun:
|
||||
`docs/generated/xr/xr_windowed_interactive_desktop_probe_2026-07-02.json`
|
||||
- that bridged rerun proves the remaining live XR lane can now be launched in
|
||||
Session `1` with `isInteractiveDesktopSession: true`
|
||||
- the remaining blocker is therefore narrowed to absent active `OpenXR`
|
||||
runtime, not to the Windows desktop-session bridge anymore
|
||||
- the current remaining unproven pieces therefore are:
|
||||
- live packaged headset-session observation on the Windows lane
|
||||
- live packaged controller-input observation on the Windows lane
|
||||
|
|
@ -500,5 +512,7 @@ first clean remaining implementation packet should be:
|
|||
owner plus controller settings/rebinding substrate and above the already-
|
||||
shipped headless `nullrhi` packaged structural proof, and run from the
|
||||
interactive logged-in Windows desktop session with an active local `OpenXR`
|
||||
runtime rather than from the reverse-SSH/OpenSSH Session `0` lane
|
||||
runtime; that session may now be reached either directly on the desktop or
|
||||
through the new first-party interactive-desktop task bridge above the
|
||||
reverse-SSH Session `0` lane
|
||||
2. public/product copy update only after that live packaged proof exists
|
||||
|
|
|
|||
|
|
@ -2502,6 +2502,44 @@ Latest same-family responsive public-route expansion follow-up on `2026-06-29`:
|
|||
powershell -NoProfile -ExecutionPolicy Bypass -File C:\HyperTwist_worktrees\phase10validate\scripts\Invoke-HyperTwistXrWindowedObservation.ps1 -PackageRoot C:\HyperTwist_worktrees\phase10validate_packaged_xr_20260702_rebuild
|
||||
```
|
||||
|
||||
## XR interactive desktop bridge continuation (`2026-07-02`)
|
||||
|
||||
- the next same-family continuation then closed the remaining Session `0`
|
||||
versus Session `1` launcher gap instead of leaving the final XR gate as
|
||||
manual-desktop-only lore
|
||||
- current exact-source truth is now source-backed through:
|
||||
- `scripts/Invoke-HyperTwistInteractiveDesktopTask.ps1`
|
||||
- checked-in bridge proof:
|
||||
`docs/generated/xr/xr_interactive_desktop_task_probe_2026-07-02.json`
|
||||
- checked-in bridged XR rerun:
|
||||
`docs/generated/xr/xr_windowed_interactive_desktop_probe_2026-07-02.json`
|
||||
- the new helper now lets the reverse-SSH Session `0` lane create and run a
|
||||
bounded `INTERACTIVE` scheduled task on the same logged-in Windows desktop,
|
||||
then wait for a structured JSON result instead of leaving that desktop hop to
|
||||
manual operator timing
|
||||
- the helper proof established:
|
||||
- launcher session before bridge: `0`
|
||||
- Explorer desktop session: `1`
|
||||
- scheduled task creation: passed
|
||||
- scheduled task run: passed
|
||||
- resulting bridged task session: `1`
|
||||
- the XR windowed observation wrapper was then rerun through that bridged
|
||||
Session `1` path and the current remaining blocker narrowed again:
|
||||
- `isInteractiveDesktopSession: true`
|
||||
- `openXrActiveRuntimePresent: false`
|
||||
- failure reason now points only to the missing active `OpenXR` runtime
|
||||
- current truthful reading after this continuation:
|
||||
- HyperTwist now owns a first-party reverse-SSH-to-desktop bridge for the
|
||||
remaining live XR observation lane
|
||||
- the session-context blocker is closed
|
||||
- the remaining live XR blocker is the absent active `OpenXR` runtime on the
|
||||
logged-in Windows desktop
|
||||
- canonical bridged command shape from Session `0` is now:
|
||||
|
||||
```powershell
|
||||
& 'C:\HyperTwist_worktrees\phase10validate\scripts\Invoke-HyperTwistInteractiveDesktopTask.ps1' -InlineCommand "& 'C:\HyperTwist_worktrees\phase10validate\scripts\Invoke-HyperTwistXrWindowedObservation.ps1' -PackageRoot 'C:\HyperTwist_worktrees\phase10validate_packaged_xr_20260702_rebuild'"
|
||||
```
|
||||
|
||||
## Latest protected launch-status responsive recovery plus tooling rerun (`2026-06-29`)
|
||||
|
||||
- the next same-family browser pass fixed a real small-screen regression on the
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
233
scripts/Invoke-HyperTwistInteractiveDesktopTask.ps1
Normal file
233
scripts/Invoke-HyperTwistInteractiveDesktopTask.ps1
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$InlineCommand,
|
||||
[int]$TimeoutSeconds = 120,
|
||||
[string]$TaskNamePrefix = 'HyperTwist-InteractiveDesktop',
|
||||
[string]$ResultPath = '',
|
||||
[switch]$KeepArtifacts
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Write-Utf8JsonFile {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Path,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[object]$Value
|
||||
)
|
||||
|
||||
$ParentPath = Split-Path -Parent $Path
|
||||
if (-not [string]::IsNullOrWhiteSpace($ParentPath))
|
||||
{
|
||||
New-Item -ItemType Directory -Force -Path $ParentPath | Out-Null
|
||||
}
|
||||
|
||||
$Json = $Value | ConvertTo-Json -Depth 12
|
||||
$Utf8NoBom = New-Object System.Text.UTF8Encoding($false)
|
||||
[System.IO.File]::WriteAllText($Path, $Json, $Utf8NoBom)
|
||||
}
|
||||
|
||||
function Read-JsonFile {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
return Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json
|
||||
}
|
||||
|
||||
function Convert-TaskToken {
|
||||
param(
|
||||
[string]$Value
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($Value))
|
||||
{
|
||||
return 'unknown'
|
||||
}
|
||||
|
||||
return ($Value -replace '[^A-Za-z0-9_-]', '_')
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($InlineCommand))
|
||||
{
|
||||
throw 'Provide -InlineCommand.'
|
||||
}
|
||||
|
||||
$TaskToken = Convert-TaskToken -Value (
|
||||
'{0}-{1}' -f $TaskNamePrefix, (Get-Date).ToString('yyyyMMdd-HHmmss')
|
||||
)
|
||||
$TaskName = $TaskToken
|
||||
$TaskRunnerPath = 'C:\Windows\Temp\{0}.runner.ps1' -f $TaskToken
|
||||
$TaskResultPath = 'C:\Windows\Temp\{0}.result.json' -f $TaskToken
|
||||
$EncodedInlineCommand = [Convert]::ToBase64String(
|
||||
[System.Text.Encoding]::Unicode.GetBytes($InlineCommand)
|
||||
)
|
||||
|
||||
if (Test-Path -LiteralPath $TaskResultPath)
|
||||
{
|
||||
Remove-Item -LiteralPath $TaskResultPath -Force
|
||||
}
|
||||
|
||||
$RunnerScript = @"
|
||||
`$ErrorActionPreference = 'Stop'
|
||||
`$Result = [ordered]@{
|
||||
succeeded = `$false
|
||||
user = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
|
||||
processId = `$PID
|
||||
sessionId = (Get-Process -Id `$PID).SessionId
|
||||
explorerSessionIds = @((Get-Process explorer -ErrorAction SilentlyContinue | Select-Object -ExpandProperty SessionId -Unique))
|
||||
generatedAtUtc = [DateTime]::UtcNow.ToString('o')
|
||||
outputLines = @()
|
||||
error = `$null
|
||||
}
|
||||
try {
|
||||
`$CommandSource = [System.Text.Encoding]::Unicode.GetString(
|
||||
[Convert]::FromBase64String('$EncodedInlineCommand')
|
||||
)
|
||||
`$CapturedOutput = @(& ([scriptblock]::Create(`$CommandSource)) 2>&1 | ForEach-Object { `$_.ToString() })
|
||||
`$Result.outputLines = @(`$CapturedOutput)
|
||||
`$Result.succeeded = `$true
|
||||
} catch {
|
||||
if (`$null -ne `$CapturedOutput) {
|
||||
`$Result.outputLines = @(`$CapturedOutput)
|
||||
}
|
||||
`$Result.error = `$_.Exception.Message
|
||||
}
|
||||
[System.IO.File]::WriteAllText(
|
||||
'$TaskResultPath',
|
||||
(`$Result | ConvertTo-Json -Depth 12),
|
||||
(New-Object System.Text.UTF8Encoding(`$false))
|
||||
)
|
||||
if (-not `$Result.succeeded) {
|
||||
exit 1
|
||||
}
|
||||
"@
|
||||
|
||||
[System.IO.File]::WriteAllText(
|
||||
$TaskRunnerPath,
|
||||
$RunnerScript,
|
||||
(New-Object System.Text.UTF8Encoding($false))
|
||||
)
|
||||
|
||||
$Result = [ordered]@{
|
||||
reportVersion = 'ht-interactive-desktop-task/v1'
|
||||
generatedAtUtc = [DateTime]::UtcNow.ToString('o')
|
||||
taskName = $TaskName
|
||||
taskRunnerPath = $TaskRunnerPath
|
||||
taskResultPath = $TaskResultPath
|
||||
timeoutSeconds = $TimeoutSeconds
|
||||
currentSessionId = (Get-Process -Id $PID).SessionId
|
||||
explorerSessionIds = @(
|
||||
Get-Process explorer -ErrorAction SilentlyContinue |
|
||||
Select-Object -ExpandProperty SessionId -Unique
|
||||
)
|
||||
createExitCode = $null
|
||||
runExitCode = $null
|
||||
taskOutput = $null
|
||||
result = 'failed'
|
||||
error = $null
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$CreateArgs = @(
|
||||
'/Create', '/F',
|
||||
'/TN', $TaskName,
|
||||
'/SC', 'ONCE',
|
||||
'/ST', '23:59',
|
||||
'/SD', (Get-Date).ToString('MM/dd/yyyy'),
|
||||
'/TR', ('C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -File {0}' -f $TaskRunnerPath),
|
||||
'/RU', 'INTERACTIVE'
|
||||
)
|
||||
& schtasks.exe @CreateArgs | Out-Null
|
||||
$Result.createExitCode = $LASTEXITCODE
|
||||
if ($Result.createExitCode -ne 0)
|
||||
{
|
||||
throw "Creating the interactive desktop task failed with exit code $($Result.createExitCode)."
|
||||
}
|
||||
|
||||
& schtasks.exe /Run /TN ('\' + $TaskName) | Out-Null
|
||||
$Result.runExitCode = $LASTEXITCODE
|
||||
if ($Result.runExitCode -ne 0)
|
||||
{
|
||||
throw "Running the interactive desktop task failed with exit code $($Result.runExitCode)."
|
||||
}
|
||||
|
||||
$Deadline = [DateTime]::UtcNow.AddSeconds([Math]::Max($TimeoutSeconds, 1))
|
||||
while ([DateTime]::UtcNow -lt $Deadline)
|
||||
{
|
||||
if (Test-Path -LiteralPath $TaskResultPath)
|
||||
{
|
||||
break
|
||||
}
|
||||
|
||||
Start-Sleep -Milliseconds 500
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $TaskResultPath))
|
||||
{
|
||||
throw "The interactive desktop task did not write '$TaskResultPath' before timeout."
|
||||
}
|
||||
|
||||
$TaskOutput = Read-JsonFile -Path $TaskResultPath
|
||||
if ($null -eq $TaskOutput)
|
||||
{
|
||||
throw "The interactive desktop task result '$TaskResultPath' could not be parsed."
|
||||
}
|
||||
|
||||
$Result.taskOutput = $TaskOutput
|
||||
if (-not [bool]$TaskOutput.succeeded)
|
||||
{
|
||||
$TaskError = if ([string]::IsNullOrWhiteSpace([string]$TaskOutput.error))
|
||||
{
|
||||
'The interactive desktop task reported failure without an explicit error message.'
|
||||
}
|
||||
else
|
||||
{
|
||||
[string]$TaskOutput.error
|
||||
}
|
||||
|
||||
throw $TaskError
|
||||
}
|
||||
|
||||
$Result.result = 'passed'
|
||||
}
|
||||
catch
|
||||
{
|
||||
$Result.error = $_.Exception.Message
|
||||
if ($null -eq $Result.taskOutput -and (Test-Path -LiteralPath $TaskResultPath))
|
||||
{
|
||||
$Result.taskOutput = Read-JsonFile -Path $TaskResultPath
|
||||
}
|
||||
$ResolvedFailureResultPath = if ([string]::IsNullOrWhiteSpace($ResultPath))
|
||||
{
|
||||
$TaskResultPath
|
||||
}
|
||||
else
|
||||
{
|
||||
$ResultPath
|
||||
}
|
||||
Write-Utf8JsonFile -Path $ResolvedFailureResultPath -Value $Result
|
||||
throw
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (-not $KeepArtifacts)
|
||||
{
|
||||
& schtasks.exe /Delete /TN $TaskName /F | Out-Null
|
||||
if (Test-Path -LiteralPath $TaskRunnerPath)
|
||||
{
|
||||
Remove-Item -LiteralPath $TaskRunnerPath -Force
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($ResultPath))
|
||||
{
|
||||
$ResultPath = $TaskResultPath
|
||||
}
|
||||
|
||||
Write-Utf8JsonFile -Path $ResultPath -Value $Result
|
||||
$Result | ConvertTo-Json -Depth 12
|
||||
Loading…
Add table
Reference in a new issue