chore: import upstream snapshot with attribution
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:43:05 +08:00
commit 426e9eeabd
41828 changed files with 9656266 additions and 0 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,114 @@
#!/usr/bin/env node
/**
* Ensures platform-specific dependencies are installed for computer-use.
*
* Default driver = nutjs (cross-platform native bindings). With nutjs, no
* shell tools are required — this script exits early after confirming the
* native module loads. Set ELIZA_COMPUTERUSE_DRIVER=legacy to switch back
* to the per-OS shell drivers (cliclick / xdotool / PowerShell), in which
* case this script ensures those tools are present.
*
* macOS: installs cliclick via Homebrew (fast, reliable mouse/keyboard control)
* Linux: checks for xdotool, suggests install command
* Windows: no extra deps needed (PowerShell built-in)
*
* This runs as a postinstall hook — it's best-effort and never fails the install.
*/
import { execSync } from "node:child_process";
import { platform } from "node:os";
const driver = (process.env.ELIZA_COMPUTERUSE_DRIVER ?? "nutjs")
.trim()
.toLowerCase();
if (driver === "nutjs") {
console.log(
"[computeruse] Driver = nutjs (cross-platform). Shell-tool checks are only required for the legacy driver.",
);
console.log(
"[computeruse] Set ELIZA_COMPUTERUSE_DRIVER=legacy to switch to per-OS shell drivers.",
);
process.exit(0);
}
function commandExists(cmd) {
try {
const which = platform() === "win32" ? "where" : "which";
execSync(`${which} ${cmd}`, { stdio: "ignore", timeout: 5000 });
return true;
} catch {
return false;
}
}
function run(cmd) {
try {
execSync(cmd, { stdio: "inherit", timeout: 120000 });
return true;
} catch {
return false;
}
}
const os = platform();
if (os === "darwin") {
// macOS: install cliclick for fast, reliable mouse/keyboard control
if (!commandExists("cliclick")) {
if (commandExists("brew")) {
console.log(
"[computeruse] Installing cliclick via Homebrew (fast mouse/keyboard control)...",
);
if (run("brew install cliclick")) {
console.log("[computeruse] cliclick installed successfully.");
} else {
console.log(
"[computeruse] cliclick install failed — falling back to AppleScript (slower).",
);
console.log("[computeruse] To install manually: brew install cliclick");
}
} else {
console.log(
"[computeruse] Homebrew not found. For best performance, install cliclick:",
);
console.log(
'[computeruse] /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"',
);
console.log("[computeruse] brew install cliclick");
console.log(
"[computeruse] Falling back to AppleScript for mouse/keyboard control.",
);
}
} else {
console.log("[computeruse] cliclick already installed.");
}
} else if (os === "linux") {
// Linux: check for xdotool
if (!commandExists("xdotool")) {
console.log(
"[computeruse] xdotool not found — required for mouse/keyboard control on Linux.",
);
console.log("[computeruse] Install via: sudo apt install xdotool");
console.log("[computeruse] Or: sudo dnf install xdotool");
console.log("[computeruse] Or: sudo pacman -S xdotool");
}
// Check for screenshot tool
if (
!commandExists("import") &&
!commandExists("scrot") &&
!commandExists("gnome-screenshot")
) {
console.log("[computeruse] No screenshot tool found. Install one of:");
console.log(
"[computeruse] sudo apt install imagemagick (provides 'import')",
);
console.log("[computeruse] sudo apt install scrot");
console.log("[computeruse] sudo apt install gnome-screenshot");
}
} else if (os === "win32") {
console.log(
"[computeruse] Windows detected — using built-in PowerShell for automation.",
);
}
@@ -0,0 +1,554 @@
#!/usr/bin/env bun
/**
* #9581 — Windows non-disruptive mouse/keyboard *effect* screen RECORDING.
*
* The capture harness (`capture-windows-desktop-evidence.mjs`) proves the input
* lands by reading the typed marker back; this companion produces the moving
* picture the issue asks for — a real screen recording of CUA input taking
* effect on a controlled Windows text-input window.
*
* gdigrab is blocked in this RDP session (BitBlt error 5), but the computeruse
* capture path (WinRT/.NET) works, so we capture a dense frame burst through it
* WHILE driving mouse_move → click → paste (progressive, chunked) → save →
* select-all, verify the marker via a saved file + clipboard/UIA read-back,
* then ffmpeg the frames into an mp4 + gif. Non-disruptive: it drives a
* freshly-launched controlled window and kills it by window id, never the
* user's apps.
*
* Run: bun scripts/record-windows-cua-input.mjs
*/
import { execFileSync } from "node:child_process";
import { copyFile, mkdir, readFile, rm, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { readClipboard, writeClipboard } from "../src/platform/clipboard.ts";
import { ComputerUseService } from "../src/services/computer-use-service.ts";
const here = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(here, "../../..");
const outDir = path.join(
repoRoot,
"test-results/evidence/9581-windows-cua/input-recording",
);
const framesDir = path.join(outDir, "frames");
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
function createRuntime(settings = {}) {
return {
character: {},
getSetting: (k) => settings[k],
getService: () => null,
};
}
function displayForPoint(displays, x, y) {
return (
displays.find((d) => {
const [dx, dy, w, h] = d.bounds;
return x >= dx && x < dx + w && y >= dy && y < dy + h;
}) ??
displays.find((d) => d.primary) ??
displays[0]
);
}
function windowHaystack(w) {
return `${w?.app ?? ""} ${w?.title ?? ""}`.toLowerCase();
}
function looksLikeTargetWindow(w, targetTitle) {
return windowHaystack(w).includes(targetTitle.toLowerCase());
}
function psSingleQuote(value) {
return `'${String(value).replace(/'/g, "''")}'`;
}
function createInputTargetScript(title, savePath) {
const savedTitle = `${title} (saved)`;
return `
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$savePath = ${psSingleQuote(savePath)}
$form = New-Object System.Windows.Forms.Form
$form.Text = ${psSingleQuote(title)}
$form.Width = 920
$form.Height = 680
$form.StartPosition = 'CenterScreen'
$form.KeyPreview = $true
$box = New-Object System.Windows.Forms.TextBox
$box.Multiline = $true
$box.AcceptsReturn = $true
$box.AcceptsTab = $true
$box.ScrollBars = 'Both'
$box.WordWrap = $false
$box.Dock = 'Fill'
$box.Font = New-Object System.Drawing.Font('Consolas', 16)
$form.Controls.Add($box)
$save = {
[System.IO.File]::WriteAllText($savePath, $box.Text)
$form.Text = ${psSingleQuote(savedTitle)}
}
$box.Add_KeyDown({
if ($_.Control -and $_.KeyCode -eq [System.Windows.Forms.Keys]::S) {
& $save
$_.SuppressKeyPress = $true
}
})
$form.Add_KeyDown({
if ($_.Control -and $_.KeyCode -eq [System.Windows.Forms.Keys]::S) {
& $save
$_.SuppressKeyPress = $true
}
})
$form.Add_Shown({ $box.Focus() })
[void]$form.ShowDialog()
`;
}
function readWindowTextViaUia(windowId) {
const pid = Number.parseInt(String(windowId), 10);
if (!Number.isFinite(pid) || pid <= 0) return "";
const ps = `
Add-Type -AssemblyName UIAutomationClient
Add-Type -AssemblyName UIAutomationTypes
$proc = Get-Process -Id ${pid} -ErrorAction Stop
$root = [System.Windows.Automation.AutomationElement]::FromHandle($proc.MainWindowHandle)
if ($null -eq $root) { exit 0 }
$nodes = $root.FindAll([System.Windows.Automation.TreeScope]::Descendants, [System.Windows.Automation.Condition]::TrueCondition)
$out = New-Object System.Collections.Generic.List[string]
for ($i = 0; $i -lt $nodes.Count; $i++) {
$el = $nodes.Item($i)
try {
$pattern = $null
if ($el.TryGetCurrentPattern([System.Windows.Automation.TextPattern]::Pattern, [ref]$pattern)) {
$text = $pattern.DocumentRange.GetText(-1)
if ($text) { [void]$out.Add($text) }
}
} catch {}
try {
$pattern = $null
if ($el.TryGetCurrentPattern([System.Windows.Automation.ValuePattern]::Pattern, [ref]$pattern)) {
$value = $pattern.Current.Value
if ($value) { [void]$out.Add($value) }
}
} catch {}
try {
$name = $el.Current.Name
if ($name) { [void]$out.Add($name) }
} catch {}
}
$out -join "\`n"
`;
try {
return execFileSync("powershell", ["-NoProfile", "-Command", ps], {
encoding: "utf8",
timeout: 15_000,
stdio: ["ignore", "pipe", "ignore"],
});
} catch {
return "";
}
}
let frameIndex = 0;
async function captureFrame(service, label, frames) {
const shot = await service.executeCommand("screenshot");
if (!shot.success || !shot.screenshot) {
throw new Error(`screenshot failed: ${shot.error ?? "no payload"}`);
}
const buf = Buffer.from(shot.screenshot, "base64");
const name = `frame-${String(frameIndex).padStart(3, "0")}.png`;
await writeFile(path.join(framesDir, name), buf);
frames.push({ index: frameIndex, name, label, bytes: buf.length });
frameIndex++;
return buf.length;
}
async function main() {
await rm(outDir, { recursive: true, force: true }).catch(() => {});
await mkdir(framesDir, { recursive: true });
const token = `eliza-win-cua-${Date.now()}`;
const phrase = token;
const chunks = phrase.match(/.{1,12}/g) ?? [phrase];
const targetTitle = `elizaOS CUA Input Target ${process.pid}`;
const targetTextFile = path.join(outDir, "windows-cua-input-target.txt");
const targetScript = path.join(outDir, "windows-cua-input-target.ps1");
await writeFile(targetTextFile, "", "utf8");
await writeFile(
targetScript,
createInputTargetScript(targetTitle, targetTextFile),
"utf8",
);
const service = await ComputerUseService.start(
createRuntime({
COMPUTER_USE_APPROVAL_MODE: "full_control",
// Explicit captures only — no implicit post-action screenshots (faster,
// and we control exactly which frames make the recording).
COMPUTER_USE_SCREENSHOT_AFTER_ACTION: "false",
COMPUTER_USE_BROWSER_HEADLESS: "true",
}),
);
const frames = [];
const originalClipboard = await readClipboard().catch(() => "");
let targetWindow = null;
let verified = false;
let verificationMethod = "none";
let verificationReadback = "";
try {
const displays = service.getDisplays();
const launched = await service.executeCommand("launch", {
app: "cmd.exe",
appArgs: [
"/c",
"start",
"",
"powershell.exe",
"-NoProfile",
"-STA",
"-ExecutionPolicy",
"Bypass",
"-File",
targetScript,
],
});
if (!launched.success) {
throw new Error(
`launch input target failed: ${launched.error ?? "unknown"}`,
);
}
// Resolve the real target window by the generated title. That keeps this
// run isolated from whatever user apps or restored editor sessions are open.
for (let attempt = 0; attempt < 20; attempt++) {
const found = await service.executeWindowAction({ action: "list" });
targetWindow =
(found?.windows ?? []).find((window) =>
looksLikeTargetWindow(window, targetTitle),
) ?? null;
if (targetWindow?.id) break;
await sleep(250);
}
if (!targetWindow?.id) {
throw new Error(
`could not resolve controlled input window titled ${targetTitle}`,
);
}
await service
.executeWindowAction({ action: "focus", windowId: targetWindow.id })
.catch(() => {});
await sleep(500);
await captureFrame(
service,
"controlled text target launched (empty)",
frames,
);
// Maximize the target, then read its actual bounds. Windows can preserve a
// snapped/tiled layout even after a maximize request in this RDP session, so
// display-center clicks can land in a neighboring Edge window.
await service
.executeWindowAction({ action: "maximize", windowId: targetWindow.id })
.catch(() => {});
await sleep(700);
await captureFrame(
service,
"controlled text target maximized (empty)",
frames,
);
const bounds = await service.executeWindowAction({
action: "get_window_position",
windowId: targetWindow.id,
});
if (!bounds.success || !bounds.bounds) {
throw new Error(
`could not read target bounds: ${bounds.error ?? "unknown"}`,
);
}
const b = bounds.bounds;
// Aim at the center of the multiline text box.
const globalX = Math.round(b.x + b.width / 2);
const globalY = Math.round(b.y + b.height / 2);
const display = displayForPoint(displays, globalX, globalY);
if (!display) {
throw new Error("no display available for input target");
}
const [displayX, displayY] = display.bounds;
const coordinate = [globalX - displayX, globalY - displayY];
// Click to focus + place the caret; Ctrl+End re-homes the caret so re-clicks
// between chunks never split the text.
const focusControlledTarget = async () => {
if (!targetWindow?.id) {
throw new Error("Input target window is not resolved");
}
await service
.executeWindowAction({ action: "focus", windowId: targetWindow.id })
.catch(() => {});
await sleep(200);
};
const clickFocus = async () => {
await focusControlledTarget();
await service.executeCommand("click", {
coordinate,
displayId: display.id,
});
await sleep(250);
};
const keyComboInTarget = async (key) => {
await clickFocus();
const result = await service.executeCommand("key_combo", { key });
if (!result.success) {
throw new Error(`key_combo ${key} failed: ${result.error}`);
}
await sleep(250);
};
await service
.executeCommand("mouse_move", { coordinate, displayId: display.id })
.catch(() => {});
await clickFocus();
await captureFrame(
service,
"clicked into the text area (caret active)",
frames,
);
// Progressive, chunked typing so the recording shows text appearing.
let typedSoFar = "";
for (const chunk of chunks) {
await keyComboInTarget("ctrl+End");
await writeClipboard(chunk);
await keyComboInTarget("ctrl+v");
typedSoFar += chunk;
await captureFrame(
service,
`pasted ${typedSoFar.length}/${phrase.length} chars via ctrl+v`,
frames,
);
}
await keyComboInTarget("ctrl+s");
await sleep(700);
await captureFrame(service, "ctrl+s saved the typed text file", frames);
// Ask for select-all (visible when the legacy SendKeys driver manages to
// keep focus), then verify by clipboard. On this Windows/Edge split-screen
// session SendKeys can leave focus on the text area without a visible
// selection, so fall back to UI Automation text readback from the controlled
// target window. These methods read the real app/window, not a mock.
await keyComboInTarget("ctrl+a");
await captureFrame(service, "ctrl+a requested for the typed text", frames);
await sleep(300);
await keyComboInTarget("ctrl+a");
await keyComboInTarget("ctrl+c");
await sleep(350);
const fileReadBack = await readFile(targetTextFile, "utf8").catch(() => "");
const readBack = await readClipboard().catch(() => "");
const uiaReadBack = readWindowTextViaUia(targetWindow.id);
const freshWindows = await service.executeCommand("list_windows");
const titleReadBack = Array.isArray(freshWindows.windows)
? freshWindows.windows
.filter((window) => looksLikeTargetWindow(window, targetTitle))
.map((window) => `${window.title ?? ""} ${window.app ?? ""}`.trim())
.join("\n")
: "";
if (fileReadBack.includes(token)) {
verificationReadback = fileReadBack;
verificationMethod = "saved-file";
} else if (readBack.includes(token)) {
verificationReadback = readBack;
verificationMethod = "clipboard";
} else if (uiaReadBack.includes(token)) {
verificationReadback = uiaReadBack;
verificationMethod = "uia";
} else {
verificationReadback = titleReadBack;
verificationMethod = "window-title";
}
verified = verificationReadback.includes(token);
await captureFrame(
service,
verified
? `verified: marker read back from target via ${verificationMethod}`
: "read-back did NOT contain the marker",
frames,
);
if (!verified) {
throw new Error(
`read-back missing marker; clipboard=${JSON.stringify(
readBack.slice(0, 80),
)}; savedFile=${JSON.stringify(
fileReadBack.slice(0, 120),
)}; uia=${JSON.stringify(
uiaReadBack.slice(0, 120),
)}; title=${JSON.stringify(titleReadBack.slice(0, 120))}`,
);
}
} finally {
if (targetWindow?.id) {
const killed = await service
.executeCommand("kill_app", { target: String(targetWindow.id) })
.catch(() => ({ success: false }));
if (!killed.success) {
await service
.executeCommand("close_window", { windowId: targetWindow.id })
.catch(() => {});
}
}
await writeClipboard(originalClipboard).catch(() => {});
await service.stop().catch(() => {});
}
// Assemble the frame burst into an mp4 + gif (ffmpeg reads PNG files, not the
// blocked gdigrab device). ~2.5 fps reads as a clear step-by-step recording.
const fps = "5/2";
const pattern = path.join(framesDir, "frame-%03d.png");
const mp4 = path.join(outDir, "windows-cua-input.mp4");
const gif = path.join(outDir, "windows-cua-input.gif");
const palette = path.join(outDir, "palette.png");
execFileSync(
"ffmpeg",
[
"-y",
"-framerate",
fps,
"-i",
pattern,
"-vf",
"scale=1100:-2:flags=lanczos,format=yuv420p",
"-r",
"12",
mp4,
],
{ stdio: "pipe" },
);
execFileSync(
"ffmpeg",
[
"-y",
"-framerate",
fps,
"-i",
pattern,
"-vf",
"scale=1000:-1:flags=lanczos,palettegen",
palette,
],
{ stdio: "pipe" },
);
execFileSync(
"ffmpeg",
[
"-y",
"-framerate",
fps,
"-i",
pattern,
"-i",
palette,
"-lavfi",
"scale=1000:-1:flags=lanczos[x];[x][1:v]paletteuse",
"-r",
"6",
gif,
],
{ stdio: "pipe" },
);
await rm(palette, { force: true }).catch(() => {});
const initialFrame = frames[1] ?? frames[0];
const finalFrame = frames[frames.length - 1];
const initialStill = path.join(outDir, "initial-empty-input-window.png");
const finalStill = path.join(outDir, "final-typed-selected.png");
if (initialFrame) {
await copyFile(path.join(framesDir, initialFrame.name), initialStill);
}
if (finalFrame) {
await copyFile(path.join(framesDir, finalFrame.name), finalStill);
}
const readme = `# #9581 — Windows non-disruptive mouse/keyboard effect screen recording
The capture harness proves Windows CUA input lands by reading the typed marker
back. This is the moving-picture companion: a real screen recording of CUA mouse
and keyboard input taking effect on a controlled Windows text-input window.
Captured on a real Windows 11 Pro host (QEMU), 1728x1052, via
\`plugins/plugin-computeruse/scripts/record-windows-cua-input.mjs\`:
1. launch a generated Windows Forms text target through computeruse
2. click inside the controlled text box bounds
3. progressively paste a marker with \`Ctrl+V\` while capturing frames
4. save with \`Ctrl+S\` and verify the marker from the real saved file/window
Frames are captured through the computeruse WinRT/.NET capture path and
assembled into MP4/GIF with ffmpeg.
| File | What it is |
|------|------------|
| \`windows-cua-input.gif\` | Inline recording of the run. |
| \`windows-cua-input.mp4\` | Same recording, H.264. |
| \`final-typed-selected.png\` | Final frame after verification; the typed marker is visible in the target. |
| \`initial-empty-input-window.png\` | Early frame before typing. |
| \`windows-cua-input-target.txt\` | The real text file saved by the \`Ctrl+S\` step. |
| \`windows-cua-input-target.ps1\` | The generated controlled Windows Forms target used for the run. |
| \`recording-summary.json\` | Run metadata, including \`verified: true\` and verification method. |
Verification method for this run: \`${verificationMethod}\`.
`;
await writeFile(path.join(outDir, "README.md"), readme);
const summary = {
issue: 9581,
capturedAt: new Date().toISOString(),
host: "Windows 11 Pro (QEMU)",
verified,
verificationMethod,
verificationReadbackPreview: verificationReadback.slice(0, 240),
marker: token,
phrase,
frameCount: frames.length,
frames,
artifacts: {
mp4: path.relative(repoRoot, mp4),
gif: path.relative(repoRoot, gif),
initialStill: path.relative(repoRoot, initialStill),
finalStill: path.relative(repoRoot, finalStill),
savedTextFile: path.relative(repoRoot, targetTextFile),
targetScript: path.relative(repoRoot, targetScript),
readme: path.relative(repoRoot, path.join(outDir, "README.md")),
framesDir: path.relative(repoRoot, framesDir),
},
};
await writeFile(
path.join(outDir, "recording-summary.json"),
`${JSON.stringify(summary, null, 2)}\n`,
);
console.log(
JSON.stringify(
{
status: verified ? "passed" : "failed",
...summary.artifacts,
frameCount: frames.length,
},
null,
2,
),
);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
@@ -0,0 +1,160 @@
#!/usr/bin/env node
/**
* Validates an iOS device-evidence manifest against the required check-id
* contract (ReplayKit foreground start, broadcast-extension handshake, Vision
* OCR, app-intent invocations, Foundation-model generation, memory-pressure
* probe). Fails when a required check is missing or carries a status outside
* the allowed set.
*/
import { readFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
const REQUIRED_CHECK_IDS = new Set([
"probe",
"replayKitForegroundStart",
"broadcastExtensionHandshake",
"visionOcr",
"appIntentList",
"appIntentInvokeSafari",
"appIntentInvokeMessages",
"accessibilitySnapshot",
"foundationModelGenerate",
"memoryPressureProbe",
]);
const COMPLETE_STATUSES = new Set(["passed", "blocked_by_platform"]);
const ALLOWED_STATUSES = new Set([
"requires_device_evidence",
"passed",
"failed",
"blocked_by_platform",
]);
function usage() {
return [
"Usage: node scripts/validate-ios-device-evidence.mjs [--require-complete] [manifest]",
"",
"Validates docs/ios-device-validation.json. With --require-complete, every",
"check must have passed or be explicitly blocked by platform behavior, and",
"top-level device/build/validator evidence must be present.",
].join("\n");
}
function fail(message) {
console.error(`[ios-device-evidence] ${message}`);
process.exitCode = 1;
}
function isRecord(value) {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function nonEmptyString(value) {
return typeof value === "string" && value.trim().length > 0;
}
const args = process.argv.slice(2);
let requireComplete = false;
let manifestArg;
for (const arg of args) {
if (arg === "--help" || arg === "-h") {
console.log(usage());
process.exit(0);
}
if (arg === "--require-complete") {
requireComplete = true;
continue;
}
if (manifestArg) {
fail(`unexpected argument: ${arg}`);
} else {
manifestArg = arg;
}
}
const here = path.dirname(fileURLToPath(import.meta.url));
const manifestPath =
manifestArg ?? path.resolve(here, "../docs/ios-device-validation.json");
const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
if (!isRecord(manifest)) fail("manifest must be an object");
if (manifest.schemaVersion !== 1) fail("schemaVersion must be 1");
if (manifest.platform !== "ios") fail('platform must be "ios"');
if (!ALLOWED_STATUSES.has(manifest.status)) {
fail(`invalid top-level status: ${manifest.status}`);
}
if (!isRecord(manifest.target)) fail("target must be an object");
for (const key of ["minimumIos", "primaryIos", "minimumDeviceClass"]) {
if (!nonEmptyString(manifest.target[key])) {
fail(`target.${key} must be a non-empty string`);
}
}
if (!isRecord(manifest.evidence)) fail("evidence must be an object");
if (!Array.isArray(manifest.evidence.artifacts)) {
fail("evidence.artifacts must be an array");
}
if (!Array.isArray(manifest.checks)) fail("checks must be an array");
const seen = new Set();
for (const check of manifest.checks) {
if (!isRecord(check)) {
fail("each check must be an object");
continue;
}
if (!REQUIRED_CHECK_IDS.has(check.id)) {
fail(`unexpected check id: ${check.id}`);
}
if (seen.has(check.id)) {
fail(`duplicate check id: ${check.id}`);
}
seen.add(check.id);
if (!nonEmptyString(check.method)) {
fail(`check ${check.id} missing method`);
}
if (!ALLOWED_STATUSES.has(check.status)) {
fail(`check ${check.id} has invalid status: ${check.status}`);
}
if (
!Array.isArray(check.requiredEvidence) ||
check.requiredEvidence.length === 0 ||
!check.requiredEvidence.every(nonEmptyString)
) {
fail(`check ${check.id} must list requiredEvidence strings`);
}
}
for (const id of REQUIRED_CHECK_IDS) {
if (!seen.has(id)) fail(`missing check id: ${id}`);
}
if (requireComplete) {
for (const key of [
"deviceModel",
"iosVersion",
"buildId",
"validatedAt",
"validator",
]) {
if (!nonEmptyString(manifest.evidence[key])) {
fail(`--require-complete needs evidence.${key}`);
}
}
if (manifest.evidence.artifacts.length === 0) {
fail("--require-complete needs at least one evidence artifact");
}
for (const check of manifest.checks) {
if (!COMPLETE_STATUSES.has(check.status)) {
fail(`--require-complete: check ${check.id} is ${check.status}`);
}
}
}
if (process.exitCode) {
process.exit(process.exitCode);
}
console.log(
`[ios-device-evidence] ${manifest.checks.length} checks validated (${manifest.status})`,
);
@@ -0,0 +1,351 @@
#!/usr/bin/env node
/**
* Validates a desktop/mobile platform evidence manifest against its per-platform
* contract (required target keys and complete-evidence keys), enforcing that
* every check reports an allowed status and that passed platforms carry the
* device metadata the contract demands.
*/
import { readFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
const COMPLETE_STATUSES = new Set(["passed", "blocked_by_platform"]);
const ALLOWED_STATUSES = new Set([
"requires_device_evidence",
"passed",
"failed",
"blocked_by_platform",
]);
const PLATFORM_CONTRACTS = {
ios: {
label: "ios-device-evidence",
defaultManifest: "docs/ios-device-validation.json",
targetKeys: ["minimumIos", "primaryIos", "minimumDeviceClass"],
completeEvidenceKeys: [
"deviceModel",
"iosVersion",
"buildId",
"validatedAt",
"validator",
],
checkIds: [
"probe",
"replayKitForegroundStart",
"broadcastExtensionHandshake",
"visionOcr",
"appIntentList",
"appIntentInvokeSafari",
"appIntentInvokeMessages",
"accessibilitySnapshot",
"foundationModelGenerate",
"memoryPressureProbe",
],
},
"android-consumer": {
label: "android-device-evidence",
defaultManifest: "docs/android-device-validation.json",
targetKeys: [
"minimumApi",
"minimumAndroid",
"requiredBuildFlavor",
"distribution",
],
completeEvidenceKeys: [
"deviceModel",
"androidVersion",
"apiLevel",
"buildId",
"validatedAt",
"validator",
],
checkIds: [
"permissionsSetup",
"accessibilityTree",
"gestureDispatch",
"globalActions",
"mediaProjectionCapture",
"usageStatsEnumeration",
"cameraCapture",
"memoryPressureDispatch",
"appActionsShortcuts",
"lifeOpsScheduledTaskHandoff",
],
},
"android-aosp": {
label: "android-aosp-evidence",
defaultManifest: "docs/android-aosp-validation.json",
targetKeys: [
"minimumApi",
"imageType",
"requiredBuildFlavor",
"privilegedPermissions",
],
completeEvidenceKeys: [
"imageName",
"androidVersion",
"apiLevel",
"buildId",
"validatedAt",
"validator",
],
checkIds: [
"assistantRole",
"assistVoiceCommand",
"privilegedCapture",
"privilegedInput",
"processEnumeration",
"serviceFlavorSeparation",
"consumerBuildStripping",
"lifeOpsPersistence",
],
},
"macos-desktop": {
label: "macos-desktop-evidence",
defaultManifest: "docs/macos-desktop-validation.json",
targetKeys: ["minimumMacos", "requiredPermissions", "driver"],
completeEvidenceKeys: [
"machineModel",
"macosVersion",
"buildId",
"validatedAt",
"validator",
],
checkIds: [
"capabilityProbe",
"screenRecordingPermission",
"screenshotCapture",
"accessibilityPermission",
"mouseKeyboardInput",
"windowListFocus",
"browserAutomation",
"clipboardRoundTrip",
"approvalMode",
],
},
"linux-desktop": {
label: "linux-desktop-evidence",
defaultManifest: "docs/linux-desktop-validation.json",
targetKeys: ["minimumDistribution", "displayServer", "driver"],
completeEvidenceKeys: [
"machineId",
"distribution",
"kernelVersion",
"displayServer",
"buildId",
"validatedAt",
"validator",
],
checkIds: [
"capabilityProbe",
"dependencyProbe",
"screenshotCapture",
"mouseKeyboardInput",
"windowListFocus",
"browserAutomation",
"clipboardRoundTrip",
"terminalSafety",
"approvalMode",
],
},
"windows-desktop": {
label: "windows-desktop-evidence",
defaultManifest: "docs/windows-desktop-validation.json",
targetKeys: ["minimumWindows", "driver", "shell"],
completeEvidenceKeys: [
"machineModel",
"windowsVersion",
"buildId",
"validatedAt",
"validator",
],
checkIds: [
"capabilityProbe",
"screenshotCapture",
"mouseKeyboardInput",
"windowListFocus",
"browserAutomation",
"clipboardRoundTrip",
"terminalSafety",
"approvalMode",
"windowsHardeningRegression",
],
},
};
function usage() {
return [
"Usage: node scripts/validate-platform-evidence.mjs [--require-complete] [manifest ...]",
"",
"Validates browser/computer-use platform evidence manifests. With no",
"manifest arguments, validates all default manifests. With --require-complete,",
"every check must have passed or be explicitly blocked by platform behavior,",
"and top-level device/build/validator evidence must be present.",
].join("\n");
}
function isRecord(value) {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function nonEmptyString(value) {
return typeof value === "string" && value.trim().length > 0;
}
function nonEmptyStringArray(value) {
return (
Array.isArray(value) && value.length > 0 && value.every(nonEmptyString)
);
}
function hasEvidenceValue(value) {
return nonEmptyString(value) || nonEmptyStringArray(value);
}
function relativeFromHere(here, value) {
return path.isAbsolute(value) ? value : path.resolve(here, "..", value);
}
function parseArgs(argv) {
const manifests = [];
let requireComplete = false;
for (const arg of argv) {
if (arg === "--help" || arg === "-h") {
console.log(usage());
process.exit(0);
}
if (arg === "--require-complete") {
requireComplete = true;
continue;
}
manifests.push(arg);
}
return { manifests, requireComplete };
}
function validateManifest(manifest, manifestPath, requireComplete) {
const failures = [];
const fail = (message) => failures.push(`${manifestPath}: ${message}`);
if (!isRecord(manifest)) {
fail("manifest must be an object");
return failures;
}
if (manifest.schemaVersion !== 1) fail("schemaVersion must be 1");
if (!nonEmptyString(manifest.platform)) fail("platform must be a string");
const contract = PLATFORM_CONTRACTS[manifest.platform];
if (!contract) {
fail(`unsupported platform: ${manifest.platform}`);
return failures;
}
if (!ALLOWED_STATUSES.has(manifest.status)) {
fail(`invalid top-level status: ${manifest.status}`);
}
if (!isRecord(manifest.target)) {
fail("target must be an object");
} else {
for (const key of contract.targetKeys) {
if (!hasEvidenceValue(manifest.target[key])) {
fail(`target.${key} must be a non-empty string or string array`);
}
}
}
if (!isRecord(manifest.evidence)) {
fail("evidence must be an object");
} else if (!Array.isArray(manifest.evidence.artifacts)) {
fail("evidence.artifacts must be an array");
}
if (!Array.isArray(manifest.checks)) {
fail("checks must be an array");
return failures;
}
const requiredCheckIds = new Set(contract.checkIds);
const seen = new Set();
for (const check of manifest.checks) {
if (!isRecord(check)) {
fail("each check must be an object");
continue;
}
if (!requiredCheckIds.has(check.id))
fail(`unexpected check id: ${check.id}`);
if (seen.has(check.id)) fail(`duplicate check id: ${check.id}`);
seen.add(check.id);
if (!nonEmptyString(check.method)) fail(`check ${check.id} missing method`);
if (!ALLOWED_STATUSES.has(check.status)) {
fail(`check ${check.id} has invalid status: ${check.status}`);
}
if (
!Array.isArray(check.requiredEvidence) ||
check.requiredEvidence.length === 0 ||
!check.requiredEvidence.every(nonEmptyString)
) {
fail(`check ${check.id} must list requiredEvidence strings`);
}
}
for (const id of requiredCheckIds) {
if (!seen.has(id)) fail(`missing check id: ${id}`);
}
if (requireComplete) {
for (const key of contract.completeEvidenceKeys) {
if (!hasEvidenceValue(manifest.evidence?.[key])) {
fail(`--require-complete needs evidence.${key}`);
}
}
if (
!Array.isArray(manifest.evidence?.artifacts) ||
manifest.evidence.artifacts.length === 0
) {
fail("--require-complete needs at least one evidence artifact");
}
for (const check of manifest.checks) {
if (!COMPLETE_STATUSES.has(check.status)) {
fail(`--require-complete: check ${check.id} is ${check.status}`);
}
}
}
return failures;
}
const here = path.dirname(fileURLToPath(import.meta.url));
const { manifests, requireComplete } = parseArgs(process.argv.slice(2));
const manifestPaths =
manifests.length > 0
? manifests.map((manifest) => relativeFromHere(here, manifest))
: Object.values(PLATFORM_CONTRACTS).map((contract) =>
path.resolve(here, "..", contract.defaultManifest),
);
const failures = [];
const summaries = [];
for (const manifestPath of manifestPaths) {
let manifest;
try {
manifest = JSON.parse(await readFile(manifestPath, "utf8"));
} catch (error) {
failures.push(
`${manifestPath}: failed to read/parse JSON: ${
error instanceof Error ? error.message : String(error)
}`,
);
continue;
}
failures.push(...validateManifest(manifest, manifestPath, requireComplete));
const contract = PLATFORM_CONTRACTS[manifest?.platform];
if (contract && Array.isArray(manifest.checks)) {
summaries.push(
`[${contract.label}] ${manifest.checks.length} checks validated (${manifest.status})`,
);
}
}
if (failures.length > 0) {
for (const failure of failures) console.error(failure);
process.exit(1);
}
for (const summary of summaries) console.log(summary);