tstest/natlab/vmtest: add macOS VM screenshot streaming to web UI

When --vmtest-web is set, Host.app is launched with --screenshot-port 0
to start a localhost HTTP server that captures the VZVirtualMachineView
display. The Go test harness parses the SCREENSHOT_PORT=<port> line from
stdout, then polls every 2 seconds for JPEG thumbnails and pushes them
over WebSocket to the web dashboard.

Clicking a screenshot thumbnail opens a full-resolution image proxied
through the web UI's /screenshot/{node} endpoint.

Screenshot events are excluded from the EventBus history (they're large
and only the latest matters, stored in NodeStatus.Screenshot).

Updates #13038

Change-Id: I9bc67ddd1cc72948b33c555d4be3d8db06a41f6d
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2026-04-28 12:46:39 -07:00
committed by Brad Fitzpatrick
parent 78627c132f
commit 4cec06b8f2
9 changed files with 168 additions and 21 deletions
+2 -2
View File
@@ -5,12 +5,12 @@ endif
.PHONY: tailmac
tailmac:
xcodebuild -scheme tailmac -destination 'platform=macOS,arch=arm64' -derivedDataPath build -configuration Release build | $(XCPRETTIFIER)
set -o pipefail && xcodebuild -scheme tailmac -destination 'platform=macOS,arch=arm64' -derivedDataPath build -configuration Release build | $(XCPRETTIFIER)
cp -r ./build/Build/Products/Release/tailmac ./bin/tailmac
.PHONY: host
host:
xcodebuild -scheme host -destination 'platform=macOS,arch=arm64' -derivedDataPath build -configuration Release build | $(XCPRETTIFIER)
set -o pipefail && xcodebuild -scheme host -destination 'platform=macOS,arch=arm64' -derivedDataPath build -configuration Release build | $(XCPRETTIFIER)
cp -r ./build/Build/Products/Release/Host.app ./bin/Host.app
.PHONY: clean
-6
View File
@@ -202,12 +202,6 @@ class ScreenshotHTTPServer: NSObject {
let n = read(fd, &buf, buf.count)
let requestLine = n > 0 ? String(bytes: buf[..<n], encoding: .utf8) ?? "" : ""
// Route: POST /keypress?key=<keycode> send a key event to the VM.
if requestLine.contains("/keypress") {
handleKeypress(fd, requestLine)
return
}
// Route: GET /screenshot capture the VM display.
let wantFull = requestLine.contains("full=1")