openclaw/test/scripts/mac-elevation-host.test.ts
Peter Steinberger 6711815b75
fix(macos): recover rejected AI setup tests without discarding pending writes (#138814)
* fix(onboarding): recover rejected AI tests without releasing uncertain writes

Record finalized pre-promotion probe rejection at the activation owner and carry
it through the direct and wizard protocols. Native setup can retry known
rejections while preserving exact-owner leases for ambiguous errors, lost
replies, replacement attempts, and potentially committed settings. Keep Check
again observational and fence late UI continuations after awaited validation.

Retire stale wizard progress before terminal settlement and preserve the
persistent-effect guard through provider capability consent. No discovery,
configuration, SQLite schema, or protocol-version change is introduced.

Repair the generated CI checkout bootstrap's large-heredoc pipe deadlock found
during verification, preserving the canonical Python owner and its deadlines.

Release-note context: native macOS AI setup now recovers rejected connection
tests without treating every terminal failure as proof that settings were not
saved. Runtime plugin preparation may remain persisted.
Refs: https://github.com/openclaw/openclaw/issues/138660

* build(ci): refresh generated checkout bootstrap after main integration

* test(ios): await Watch receipt transport events

Replace the two missed main-actor polling waits with the existing buffered send event, retaining the two-second deadline and exact transfer, receipt, and ACK assertions. Covers the original-order failure in CI run 33941478427 without changing production behavior.

* fix(macos): restore explicit setup choices after pending recovery

Keep initial automatic setup distinct from observation and pending-attempt
recovery. Preserve that intent across transport blockers and actual deadline
callbacks so a legacy Gateway's generic terminal error cannot start another
automatic test of the same failed credential after the wait expires.

Retain exact receipt ownership, live verification, and admission/cancellation
fences. Restore choices with the failure visible; require explicit selection
to start another activation. Initial no-receipt setup still auto-tests.

The mounted-view deadline regression fails on the preceding commit in both
missing-model and configured-but-invalid cases (21 assertions), then passes
with all 108 signed native tests in the same isolated Mac. Six sibling cases
cover initial/prior-attempt retry, reconnect, and inspection. Update operator
docs and remove the duplicate intent/blocker setter (net -15 production LOC).

Refs: https://github.com/openclaw/openclaw/issues/138660
Refs: https://github.com/openclaw/openclaw/pull/138814

* test(macos): emit elevation helper without a heredoc pipe

Two identity-checked stack captures caught mock ditto blocked in Bash
heredoc_write before the installer entered custody. Emit the same helper
through builtin printf and the existing quoteCliArg instead of pre-filling
a pipe before its reader starts.

Independently verify all 3,842 bytes against the prior template under both
system and Homebrew Bash. Keep installer production, shell/child boundaries,
fault injection, ordering, assertions, and timeouts unchanged.

Refs: https://github.com/openclaw/openclaw/pull/138814
2026-09-05 01:55:41 -07:00

4745 lines
195 KiB
TypeScript

import { createHash } from "node:crypto";
// Mac Elevation Host tests protect the unattended launchd and artifact contracts.
import {
appendFileSync,
chmodSync,
existsSync,
lstatSync,
mkdirSync,
readFileSync,
readdirSync,
renameSync,
rmSync,
symlinkSync,
writeFileSync,
} from "node:fs";
import path from "node:path";
import { describe, expect } from "vitest";
import { quoteCliArg } from "../../src/cli/quote-cli-arg.js";
import { createMacScriptTest, type MacScriptFixture } from "./mac-script-fixture.test-support.js";
const it = createMacScriptTest();
const scriptPath = "scripts/mac-elevation-host.sh";
const codesignScriptPath = "scripts/codesign-mac-app.sh";
function writeExecutable(filePath: string, contents: string): void {
writeFileSync(filePath, contents, "utf8");
chmodSync(filePath, 0o755);
}
function commandFixturesPath(binDir: string): string {
return path.join(binDir, "command-fixtures.bash");
}
function writeCommandFixture(
binDir: string,
command: string,
contents: string,
preserveChildBoundary = false,
): void {
writeExecutable(path.join(binDir, command), contents);
if (!preserveChildBoundary) {
appendFileSync(commandFixturesPath(binDir), [`${command}() (`, contents, ")", ""].join("\n"));
}
}
function writeLipoFixture(binDir: string): void {
writeCommandFixture(
binDir,
"lipo",
[
"#!/bin/sh",
'if [ "$1" = -info ]; then',
' printf "Architectures in the fat file: %s are: x86_64 arm64\\n" "$2"',
"else",
" printf '%s\\n' 'x86_64 arm64'",
"fi",
"",
].join("\n"),
);
}
function writeDiskutilFixture(binDir: string): void {
writeCommandFixture(
binDir,
"diskutil",
[
"#!/bin/sh",
"set -eu",
'if [ "$#" -ne 3 ] || [ "$1" != "info" ] || [ "$2" != "-plist" ]; then',
" printf '%s\\n' 'unexpected diskutil invocation' >&2",
" exit 64",
"fi",
'[ "$3" = "/dev/openclaw-test-volume" ] || {',
" printf '%s\\n' 'unexpected diskutil device' >&2",
" exit 64",
"}",
"printf '%s\\n' '<?xml version=\"1.0\" encoding=\"UTF-8\"?>' '<plist version=\"1.0\"><dict><key>VolumeUUID</key><string>00000000-0000-4000-8000-000000000001</string></dict></plist>'",
"",
].join("\n"),
);
}
function writeDfFixture(binDir: string): void {
writeCommandFixture(
binDir,
"df",
[
"#!/bin/sh",
"set -eu",
'[ "$#" -eq 2 ] && [ "$1" = "-P" ] || {',
" printf '%s\\n' 'unexpected df invocation' >&2",
" exit 64",
"}",
'if [ "${TEST_FAIL_UNSAFE_ENTRY_IDENTITY:-0}" = "1" ] && [ "$2" = "${TEST_INSTALLED_APP_PATH:-}" ]; then',
" exit 7",
"fi",
'case "$2" in',
' "$TEST_FIXTURE_ROOT"|"$TEST_FIXTURE_ROOT"/*) ;;',
" *) printf '%s\\n' 'unexpected df target' >&2; exit 64 ;;",
"esac",
"printf '%s\\n' 'Filesystem 512-blocks Used Available Capacity Mounted on'",
"printf '%s %s\\n' '/dev/openclaw-test-volume 1 1 1 1%' \"$TEST_FIXTURE_ROOT\"",
"",
].join("\n"),
);
}
function writeShasumFixture(binDir: string): void {
writeCommandFixture(
binDir,
"shasum",
[
"#!/bin/sh",
"set -eu",
'[ "$#" -ge 2 ] && [ "$1" = "-a" ] && [ "$2" = "256" ] || {',
" printf '%s\\n' 'unexpected shasum invocation' >&2",
" exit 64",
"}",
"shift 2",
"[ \"$#\" -le 1 ] || { printf '%s\\n' 'unexpected shasum target' >&2; exit 64; }",
"[ \"$#\" -eq 0 ] || [ -f \"$1\" ] || { printf '%s\\n' 'missing shasum target' >&2; exit 64; }",
'exec /usr/bin/openssl dgst -sha256 -r "$@"',
"",
].join("\n"),
);
}
function sha256(contents: string | Buffer): string {
return createHash("sha256").update(contents).digest("hex");
}
function fileIdentity(filePath: string): string {
const stats = lstatSync(filePath);
return `${stats.dev}:${stats.ino}`;
}
async function durableFileIdentity(
mac: MacScriptFixture,
filePath: string,
env: NodeJS.ProcessEnv,
): Promise<string> {
const script = readFileSync(scriptPath, "utf8");
const start = script.indexOf("path_identity() {");
const end = script.indexOf("read_optional_receipt_xattr()", start);
const result = await mac.run(
"/bin/bash",
[
"-c",
`set -euo pipefail\n${script.slice(start, end)}\ndurable_path_identity "$1"`,
"bash",
filePath,
],
{ encoding: "utf8", env },
);
expect(result.status, result.stderr).toBe(0);
return result.stdout.trim();
}
function receiptDigestArgs(receiptPath: string): string[] {
return ["--receipt-sha256", sha256(readFileSync(receiptPath))];
}
function quarantinedElevationAppPath(stateDir: string): string | undefined {
const container = readdirSync(stateDir).find((name) =>
name.startsWith("elevation-host.quarantined-app."),
);
return container ? path.join(stateDir, container, "OpenClaw.app") : undefined;
}
function preservedCuaAppPath(harness: {
appPath: string;
env: NodeJS.ProcessEnv;
stateDir: string;
}): string | undefined {
const quarantined = quarantinedElevationAppPath(harness.stateDir);
if (quarantined) {
const driver = path.join(quarantined, "Contents", "Resources", "cua-driver");
if (existsSync(driver) || lstatSync(driver, { throwIfNoEntry: false })?.isSymbolicLink()) {
return quarantined;
}
}
const home = harness.env.HOME!;
for (const entry of readdirSync(home)) {
if (
!entry.startsWith(`${path.basename(harness.appPath)}.rollback-elevation-host-`) &&
!entry.startsWith(`${path.basename(harness.appPath)}.failed-elevation-host-`)
) {
continue;
}
const container = path.join(home, entry);
const candidate = entry.includes(".failed-elevation-host-")
? path.join(container, "OpenClaw.app")
: container;
const driver = path.join(candidate, "Contents", "Resources", "cua-driver");
if (existsSync(driver) || lstatSync(driver, { throwIfNoEntry: false })?.isSymbolicLink()) {
return candidate;
}
}
return undefined;
}
async function runInstaller(
mac: MacScriptFixture,
installerPath: string,
args: string[],
env: NodeJS.ProcessEnv,
cwd = process.cwd(),
) {
return await mac.run("/bin/bash", [installerPath, ...args], {
cwd,
encoding: "utf8",
env,
});
}
async function runAuthenticatedMigrationInstall(
mac: MacScriptFixture,
harness: ReturnType<typeof createInstallRollbackHarness>,
env: NodeJS.ProcessEnv = harness.env,
) {
return await runInstaller(
mac,
harness.installerPath,
[
"install",
"--archive",
harness.archivePath,
"--receipt",
harness.receiptPath,
...receiptDigestArgs(harness.receiptPath),
"--app",
harness.appPath,
"--migrate-launch-agent",
harness.sourcePlist,
],
env,
);
}
async function runAuthenticatedElevationRecovery(
mac: MacScriptFixture,
harness: ReturnType<typeof createInstallRollbackHarness>,
) {
return await runInstaller(
mac,
harness.installerPath,
[
"recover",
"--archive",
harness.archivePath,
"--receipt",
harness.receiptPath,
...receiptDigestArgs(harness.receiptPath),
"--app",
harness.appPath,
"--state-dir",
harness.stateDir,
],
harness.env,
);
}
function writeAppInfoPlist(appPath: string, sourceCommit: string, peekabooCommit: string): void {
mkdirSync(path.join(appPath, "Contents", "MacOS"), { recursive: true });
writeFileSync(
path.join(appPath, "Contents", "Info.plist"),
[
'<?xml version="1.0" encoding="UTF-8"?>',
'<plist version="1.0"><dict>',
"<key>CFBundleIdentifier</key><string>ai.openclaw.mac</string>",
`<key>OpenClawGitCommit</key><string>${sourceCommit}</string>`,
`<key>PeekabooSourceCommit</key><string>${peekabooCommit}</string>`,
"<key>CFBundleShortVersionString</key><string>4.2.0</string>",
"<key>CFBundleVersion</key><string>420</string>",
"</dict></plist>",
"",
].join("\n"),
"utf8",
);
}
function createStatusHarness(mac: MacScriptFixture, permissionMode: "fail" | "invalid") {
const tempRoot = mac.createTempDir(`openclaw-elevation-status-${permissionMode}-`);
const binDir = path.join(tempRoot, "bin");
const appPath = path.join(tempRoot, "OpenClaw.app");
const stateDir = path.join(tempRoot, "state");
const configPath = path.join(stateDir, "openclaw.json");
const launchAgentsDir = path.join(tempRoot, "Library", "LaunchAgents");
mkdirSync(path.join(appPath, "Contents", "MacOS"), { recursive: true });
mkdirSync(binDir, { recursive: true });
mkdirSync(stateDir, { recursive: true });
mkdirSync(launchAgentsDir, { recursive: true });
writeFileSync(path.join(appPath, "Contents", "Info.plist"), "fixture", "utf8");
writeMockWorkerPair(path.join(appPath, "Contents", "Resources", "node-worker"), "0".repeat(40));
writeArtifactFileFixture(binDir);
writeCommandFixture(
binDir,
"stat",
'#!/bin/sh\n[ "$1" = -f ] && [ "$2" = "%R/" ] && [ "$3" = -- ] || exit 64\nprintf "%s/\\n" "$4"\n',
);
writeFileSync(configPath, "{}\n", "utf8");
writeFileSync(
path.join(launchAgentsDir, "ai.openclaw.mac.elevation-host.plist"),
"fixture",
"utf8",
);
writeFileSync(
path.join(stateDir, "elevation-host-install.json"),
JSON.stringify({
schemaVersion: 3,
kind: "openclaw-elevation-install",
transactionState: "installed",
transactionId: "00000000-0000-4000-8000-000000000001",
sourceCommit: "0".repeat(40),
peekabooCommit: `${"0".repeat(39)}1`,
archiveSha256: "a".repeat(64),
artifactReceiptSha256: "b".repeat(64),
installerSha256: "c".repeat(64),
cdhashes: { arm64: "TESTCDHASHARM64", x86_64: "TESTCDHASHX8664" },
nodeId: "fixture-node",
nodeProfile: "primary",
appPath,
stateDir,
configPath,
backupPath: "",
backupCDHashes: { arm64: "", x86_64: "" },
plistPath: path.join(launchAgentsDir, "ai.openclaw.mac.elevation-host.plist"),
previousPlist: "",
previousPlistSha256: "",
previousPlistWasLoaded: false,
previousReceipt: "",
previousReceiptSha256: "",
migration: null,
adoptedApp: { wasRunning: false, attachOnly: false },
}),
"utf8",
);
writeCommandFixture(
binDir,
"codesign",
[
"#!/usr/bin/env bash",
"set -euo pipefail",
'target="${!#}"',
'if [[ "$*" == *"--verify"* && -e "$target/Contents/invalid-signature" ]]; then',
" exit 1",
"fi",
'if [[ "$*" == *"--entitlements"* ]]; then',
" printf '%s\\n' '<plist><dict/></plist>'",
" exit 0",
"fi",
'if [[ "$*" == *"-dv"* ]]; then',
" cdhash=TESTCDHASH",
' if [[ "$*" == *"--arch arm64"* ]]; then cdhash=TESTCDHASHARM64; fi',
' if [[ "$*" == *"--arch x86_64"* ]]; then cdhash=TESTCDHASHX8664; fi',
" format='Mach-O universal (x86_64 arm64)'",
' [[ ! -d "$target" ]] || format="app bundle with Mach-O universal (x86_64 arm64)"',
" printf 'Format=%s\\nCodeDirectory v=20400\\n' \"$format\" >&2",
" printf '%s\\n' 'Authority=Developer ID Application: OpenClaw Foundation (FWJYW4S8P8)' >&2",
" printf '%s\\n' 'TeamIdentifier=FWJYW4S8P8' >&2",
" printf 'CDHash=%s\\n' \"$cdhash\" >&2",
"fi",
"exit 0",
"",
].join("\n"),
);
writeExecutable(
path.join(binDir, "launchctl"),
[
"#!/usr/bin/env bash",
"set -euo pipefail",
'if [[ "${1:-}" == "print" && "${2:-}" == */ai.openclaw.mac.elevation-host ]]; then',
" printf '%s\\n' ' pid = 4242'",
" exit 0",
"fi",
"exit 1",
"",
].join("\n"),
);
writeCommandFixture(
binDir,
"plutil",
[
"#!/usr/bin/env bash",
"set -euo pipefail",
'case "${2:-}" in',
" CFBundleIdentifier) printf '%s\\n' 'ai.openclaw.mac' ;;",
" OpenClawGitCommit) printf '%040d\\n' 0 ;;",
" PeekabooSourceCommit) printf '%040d\\n' 1 ;;",
" CFBundleShortVersionString) printf '%s\\n' '4.2.0' ;;",
" OpenClawBuildTimestamp) printf '%s\\n' '2026-08-28T00:00:00Z' ;;",
" OpenClawWorkerBuildID) printf '%s\\n' 'fixture-build' ;;",
' ProgramArguments) printf \'["%s/Contents/MacOS/OpenClaw","--elevation-host"]\\n\' "$TEST_APP_PATH" ;;',
" EnvironmentVariables.OPENCLAW_STATE_DIR) printf '%s\\n' \"$TEST_STATE_DIR\" ;;",
" EnvironmentVariables.OPENCLAW_CONFIG_PATH) printf '%s\\n' \"$TEST_CONFIG_PATH\" ;;",
" RunAtLoad|KeepAlive) printf '%s\\n' 'true' ;;",
" *) exit 1 ;;",
"esac",
"",
].join("\n"),
);
writeLipoFixture(binDir);
writeCommandFixture(binDir, "pgrep", "#!/bin/sh\nexit 1\n");
writeCommandFixture(binDir, "spctl", "#!/bin/sh\nexit 0\n");
writeCommandFixture(binDir, "xcrun", "#!/bin/sh\nexit 0\n");
writeExecutable(
path.join(binDir, "openclaw"),
'#!/bin/sh\nprintf \'%s\\n\' \'{"nodes":[{"nodeId":"fixture-node","connected":true,"connectedAtMs":20,"clientId":"openclaw-macos","clientMode":"node","uiVersion":"4.2.0","caps":["computer"],"commands":["screen.snapshot","computer.act"],"computerUse":{"version":2}}]}\'\n',
);
writeCommandFixture(
binDir,
"peekaboo",
[
"#!/usr/bin/env bash",
"set -euo pipefail",
'if [[ "${1:-}" == "bridge" ]]; then',
' printf \'%s\\n\' \'{"success":true,"data":{"selected":{"handshake":{"hostIdentity":{"processIdentifier":4242}}}}}\'',
" exit 0",
"fi",
'if [[ "${1:-}" == "permissions" ]]; then',
' if [[ "$TEST_PEEKABOO_MODE" == "fail" ]]; then exit 7; fi',
" printf '%s\\n' '{not-json'",
" exit 0",
"fi",
"exit 2",
"",
].join("\n"),
);
return {
appPath,
stateDir,
env: {
...process.env,
BASH_ENV: commandFixturesPath(binDir),
HOME: tempRoot,
PATH: `${binDir}${path.delimiter}${process.env.PATH ?? ""}`,
TEST_APP_PATH: appPath,
TEST_CONFIG_PATH: configPath,
TEST_STATE_DIR: stateDir,
TEST_PEEKABOO_MODE: permissionMode,
},
};
}
function createMigrationPlanHarness(
mac: MacScriptFixture,
launchState: "absent" | "error" | "loaded" = "absent",
) {
const tempRoot = mac.createTempDir(`openclaw-elevation-migration-${launchState}-`);
const binDir = path.join(tempRoot, "bin");
const launchAgentsDir = path.join(tempRoot, "Library", "LaunchAgents");
const appPath = path.join(tempRoot, "OpenClaw.app");
const stateDir = path.join(tempRoot, "node-state");
const configPath = path.join(stateDir, "openclaw.json");
const label = "ai.openclaw.mac.node-fixture";
const plistPath = path.join(launchAgentsDir, `${label}.plist`);
mkdirSync(binDir, { recursive: true });
mkdirSync(launchAgentsDir, { recursive: true });
mkdirSync(stateDir, { recursive: true });
mkdirSync(path.join(stateDir, "state"), { recursive: true });
writeDiskutilFixture(binDir);
writeDfFixture(binDir);
writeFileSync(configPath, "{}\n", "utf8");
writeFileSync(path.join(stateDir, "state", "openclaw.sqlite"), "fixture", "utf8");
writeFileSync(
plistPath,
[
'<?xml version="1.0" encoding="UTF-8"?>',
'<plist version="1.0"><dict>',
`<key>Label</key><string>${label}</string>`,
"<key>ProgramArguments</key><array>",
`<string>${appPath}/Contents/MacOS/OpenClaw</string>`,
"<string>--attach-only</string><string>--background-only</string>",
"</array>",
"<key>EnvironmentVariables</key><dict>",
`<key>OPENCLAW_STATE_DIR</key><string>${stateDir}</string>`,
`<key>OPENCLAW_CONFIG_PATH</key><string>${configPath}</string>`,
"</dict>",
"</dict></plist>",
"",
].join("\n"),
"utf8",
);
writeCommandFixture(
binDir,
"launchctl",
[
"#!/usr/bin/env bash",
"set -euo pipefail",
'[[ "${1:-}" == "print" ]] || exit 2',
'case "$TEST_LAUNCH_STATE" in',
" loaded) printf '%s\\n' ' pid = 4242' ; exit 0 ;;",
" absent) printf '%s\\n' 'Could not find service in domain' >&2; exit 113 ;;",
" error) printf '%s\\n' 'launchctl transport failed' >&2; exit 5 ;;",
"esac",
"",
].join("\n"),
);
writeExecutable(path.join(binDir, "defaults"), "#!/bin/sh\nprintf '%s\\n' primary\n");
writeCommandFixture(binDir, "sqlite3", "#!/bin/sh\nprintf '%s\\n' fixture-node\n");
writeExecutable(
path.join(binDir, "openclaw"),
[
"#!/usr/bin/env bash",
"set -euo pipefail",
'case "$*" in',
" *'config get gateway.mode'*) printf '%s\\n' '\"remote\"' ;;",
" *'config get gateway.remote.url'*) printf '%s\\n' '\"wss://gateway.invalid\"' ;;",
" *'config get gateway.remote.token'*) printf '%s\\n' '\"redacted\"' ;;",
" *'config get gateway.remote.password'*) exit 1 ;;",
' *\'nodes status\'*) printf \'%s\\n\' \'{"nodes":[{"nodeId":"fixture-node","paired":true,"connected":false}]}\' ;;',
" *) exit 2 ;;",
"esac",
"",
].join("\n"),
);
return {
appPath,
configPath,
label,
plistPath,
stateDir,
env: {
...process.env,
BASH_ENV: commandFixturesPath(binDir),
HOME: tempRoot,
PATH: `${binDir}${path.delimiter}${process.env.PATH ?? ""}`,
TEST_FIXTURE_ROOT: tempRoot,
TEST_LAUNCH_STATE: launchState,
},
};
}
function createCanonicalNodeMigrationHarness(mac: MacScriptFixture, nodeId = "fixture-node") {
const harness = createMigrationPlanHarness(mac, "loaded");
const binDir = path.join(harness.env.HOME, "bin");
const serviceEnvDir = path.join(harness.stateDir, "service-env");
const label = "ai.openclaw.node";
const plistPath = path.join(harness.env.HOME, "Library", "LaunchAgents", `${label}.plist`);
const envPath = path.join(serviceEnvDir, `${label}.env`);
const wrapperPath = path.join(serviceEnvDir, `${label}-env-wrapper.sh`);
const nodePath = path.join(binDir, "node-runtime");
const entrypointPath = path.join(harness.env.HOME, "openclaw", "dist", "index.js");
mkdirSync(serviceEnvDir, { recursive: true });
mkdirSync(path.dirname(entrypointPath), { recursive: true });
writeFileSync(entrypointPath, "fixture", "utf8");
writeFileSync(
envPath,
[
"# Generated by OpenClaw. Do not edit while the gateway service is installed.",
`export OPENCLAW_STATE_DIR='${harness.stateDir}'`,
`export OPENCLAW_CONFIG_PATH='${harness.configPath}'`,
"export OPENCLAW_GATEWAY_TOKEN='ignored-secret-shape'",
"",
].join("\n"),
"utf8",
);
chmodSync(envPath, 0o600);
writeExecutable(
wrapperPath,
[
"#!/bin/sh",
"set -eu",
'env_file="$1"',
"shift",
'if [ -f "$env_file" ]; then',
' . "$env_file"',
"fi",
'exec "$@"',
"",
].join("\n"),
);
chmodSync(wrapperPath, 0o700);
writeExecutable(
nodePath,
[
"#!/usr/bin/env bash",
"set -euo pipefail",
"shift",
'case "$*" in',
" *'config get gateway.mode'*) printf '%s\\n' '\"remote\"' ;;",
" *'config get gateway.remote.url'*) printf '%s\\n' '\"wss://gateway.invalid\"' ;;",
" *'config get gateway.remote.token'*) printf '%s\\n' '\"redacted\"' ;;",
" *'config get gateway.remote.password'*) exit 1 ;;",
' *\'nodes status\'*) printf \'%s\\n\' \'{"nodes":[{"nodeId":"fixture-node","paired":true,"connected":true,"connectedAtMs":10}]}\' ;;',
" *) exit 2 ;;",
"esac",
"",
].join("\n"),
);
writeFileSync(
plistPath,
[
'<?xml version="1.0" encoding="UTF-8"?>',
'<plist version="1.0"><dict>',
`<key>Label</key><string>${label}</string>`,
"<key>ProgramArguments</key><array>",
"<string>/bin/sh</string>",
`<string>${wrapperPath}</string>`,
`<string>${envPath}</string>`,
`<string>${nodePath}</string>`,
`<string>${entrypointPath}</string>`,
"<string>node</string><string>run</string><string>--host</string><string>gateway.invalid</string>",
"<string>--port</string><string>18789</string><string>--no-tls</string>",
`<string>--node-id</string><string>${nodeId}</string>`,
"</array></dict></plist>",
"",
].join("\n"),
"utf8",
);
return { ...harness, envPath, label, plistPath };
}
async function runCanonicalNodeSidecarVerifier(
mac: MacScriptFixture,
harness: ReturnType<typeof createCanonicalNodeMigrationHarness>,
envSha: string,
wrapperSha: string,
paths?: { envPath?: string; wrapperPath?: string },
) {
const script = readFileSync(scriptPath, "utf8");
const start = script.indexOf("canonical_node_wrapper_is_canonical() {");
const end = script.indexOf("background_app_records() {", start);
expect(start).toBeGreaterThanOrEqual(0);
expect(end).toBeGreaterThan(start);
const envPath = paths?.envPath ?? harness.envPath;
const wrapperPath = paths?.wrapperPath ?? `${harness.envPath.slice(0, -4)}-env-wrapper.sh`;
return await mac.run(
"/bin/bash",
[
"-c",
[
"set -euo pipefail",
`MIGRATION_KIND=canonical-node`,
`MIGRATION_NODE_ENV_PATH=${JSON.stringify(envPath)}`,
`MIGRATION_NODE_ENV_SHA=${JSON.stringify(envSha)}`,
`MIGRATION_NODE_ENV_IDENTITY=''`,
`MIGRATION_NODE_WRAPPER_PATH=${JSON.stringify(wrapperPath)}`,
`MIGRATION_NODE_WRAPPER_SHA=${JSON.stringify(wrapperSha)}`,
`MIGRATION_NODE_WRAPPER_IDENTITY=''`,
`STATE_DIR=${JSON.stringify(harness.stateDir)}`,
`CONFIG_PATH=${JSON.stringify(harness.configPath)}`,
script.slice(start, end),
"verify_canonical_node_sidecars",
].join("\n"),
],
{ encoding: "utf8", env: harness.env },
);
}
async function runMigrationReceiptBindingVerifier(
mac: MacScriptFixture,
harness: ReturnType<typeof createCanonicalNodeMigrationHarness>,
kind: "app-launch-agent" | "canonical-node",
paths?: {
envIdentity?: string;
envPath?: string;
wrapperIdentity?: string;
wrapperPath?: string;
},
) {
const envPath = paths?.envPath ?? harness.envPath;
const wrapperPath = paths?.wrapperPath ?? `${harness.envPath.slice(0, -4)}-env-wrapper.sh`;
const envSha = kind === "canonical-node" ? sha256(readFileSync(envPath)) : "";
const wrapperSha = kind === "canonical-node" ? sha256(readFileSync(wrapperPath)) : "";
const envIdentity =
kind === "canonical-node" ? (paths?.envIdentity ?? fileIdentity(envPath)) : "";
const wrapperIdentity =
kind === "canonical-node" ? (paths?.wrapperIdentity ?? fileIdentity(wrapperPath)) : "";
const script = readFileSync(scriptPath, "utf8");
const start = script.indexOf("canonical_node_wrapper_is_canonical() {");
const end = script.indexOf("background_app_records() {", start);
return await mac.run(
"/bin/bash",
[
"-c",
[
"set -euo pipefail",
'plist_file_value() { /usr/libexec/PlistBuddy -c "Print :$2" "$1"; }',
'path_matches_identity() { [[ "$(stat -f \'%d:%i\' -- "$1")" == "$2" ]]; }',
`MIGRATION_KIND=${kind}`,
`MIGRATION_NODE_ENV_PATH=${JSON.stringify(kind === "canonical-node" ? envPath : "")}`,
`MIGRATION_NODE_ENV_SHA=${JSON.stringify(envSha)}`,
`MIGRATION_NODE_ENV_IDENTITY=${JSON.stringify(envIdentity)}`,
`MIGRATION_NODE_WRAPPER_PATH=${JSON.stringify(kind === "canonical-node" ? wrapperPath : "")}`,
`MIGRATION_NODE_WRAPPER_SHA=${JSON.stringify(wrapperSha)}`,
`MIGRATION_NODE_WRAPPER_IDENTITY=${JSON.stringify(wrapperIdentity)}`,
`ROLLBACK_MIGRATION_LABEL=${JSON.stringify(harness.label)}`,
`ROLLBACK_MIGRATION_PLIST=${JSON.stringify(harness.plistPath)}`,
`APP_PATH=${JSON.stringify(harness.appPath)}`,
`STATE_DIR=${JSON.stringify(harness.stateDir)}`,
`CONFIG_PATH=${JSON.stringify(harness.configPath)}`,
script.slice(start, end),
'migration_receipt_matches_backup_plist "$ROLLBACK_MIGRATION_PLIST"',
].join("\n"),
],
{ encoding: "utf8", env: harness.env },
);
}
function addRunningAppFixture(harness: ReturnType<typeof createMigrationPlanHarness>) {
const binDir = path.join(harness.env.HOME, "bin");
const appBinary = `${harness.appPath}/Contents/MacOS/OpenClaw`;
writeCommandFixture(binDir, "pgrep", "#!/bin/sh\nprintf '%s\\n' 4242\n");
writeCommandFixture(
binDir,
"lsof",
`#!/bin/sh\nprintf '%s\\n' p4242 n${JSON.stringify(appBinary)}\n`,
);
writeCommandFixture(
binDir,
"ps",
`#!/bin/sh\nprintf '%s\\n' ${JSON.stringify(`${appBinary} --attach-only --background-only`)}\n`,
);
}
function writeMockWorkerPair(root: string, sourceCommit: string): void {
for (const arch of ["arm64", "x86_64"]) {
const worker = path.join(root, arch);
const dist = path.join(worker, "lib", "node_modules", "openclaw", "dist");
mkdirSync(path.join(worker, "bin"), { recursive: true });
mkdirSync(dist, { recursive: true });
writeExecutable(path.join(worker, "bin", "node"), "fixture-node");
writeFileSync(path.join(dist, "entry.js"), "fixture-entry");
writeFileSync(
path.join(dist, "build-info.json"),
JSON.stringify({
version: "4.2.0",
commit: sourceCommit,
builtAt: "2026-08-28T00:00:00Z",
buildId: "fixture-build",
}),
);
}
}
function writeArtifactFileFixture(binDir: string): void {
writeCommandFixture(
binDir,
"file",
[
"#!/bin/sh",
"set -eu",
'if [ "${1:-} ${2:-} ${3:-} ${4:-} ${5:-} ${6:-}" = "-E -N -r -0 -0 --" ]; then',
" framed=1; shift 6",
'elif [ "$#" -eq 3 ] && [ "$1 $2" = "-b -E" ]; then',
" framed=0; shift 2",
"else exit 64; fi",
'[ "$#" -gt 0 ] || exit 64',
'for target in "$@"; do',
'case "$target" in',
" */Contents/MacOS/OpenClaw|*/Contents/MacOS/openclaw-mlx-tts|*/node-worker/arm64/bin/node|*/node-worker/x86_64/bin/node)",
" description='Mach-O universal binary' ;;",
" *) description=data ;;",
"esac",
'if [ "$framed" -eq 1 ]; then',
' printf \'%s\\0%s\\0\' "$target" "$description"',
"else printf '%s\\n' \"$description\"; fi",
"done",
"",
].join("\n"),
);
}
function createArtifactVerificationHarness(mac: MacScriptFixture) {
const tempRoot = mac.createTempDir("openclaw-elevation-artifact-");
const binDir = path.join(tempRoot, "bin");
const archivePath = path.join(tempRoot, "OpenClaw-fixture-stable.zip");
const installerPath = path.join(tempRoot, "OpenClaw-fixture-stable-installer.sh");
const receiptPath = path.join(tempRoot, "OpenClaw-fixture-stable.json");
const dittoMarker = path.join(tempRoot, "ditto-called");
const sourceCommit = "a".repeat(40);
const peekabooCommit = "b".repeat(40);
const entitlements = "<plist><dict/></plist>\n";
mkdirSync(binDir, { recursive: true });
const workers = path.join(tempRoot, "worker-pair");
writeMockWorkerPair(workers, sourceCommit);
writeShasumFixture(binDir);
writeFileSync(archivePath, "not-a-real-zip-but-deterministic", "utf8");
writeExecutable(installerPath, readFileSync(scriptPath, "utf8"));
writeCommandFixture(
binDir,
"ditto",
[
"#!/usr/bin/env bash",
"set -euo pipefail",
': >"$TEST_DITTO_MARKER"',
'if [[ "$#" == "2" ]]; then',
' /usr/bin/ditto "$1" "$2"',
" exit 0",
"fi",
'destination="${4}"',
'app="$destination/OpenClaw.app"',
'mkdir -p "$app/Contents/MacOS"',
'mkdir -p "$app/Contents/Resources"',
'cp -R "$TEST_ARTIFACT_WORKERS" "$app/Contents/Resources/node-worker"',
'printf \'%s\\n\' \'<?xml version="1.0" encoding="UTF-8"?>\' \'<plist version="1.0"><dict>\' >"$app/Contents/Info.plist"',
"printf '%s\\n' '<key>CFBundleIdentifier</key><string>ai.openclaw.mac</string>' >>\"$app/Contents/Info.plist\"",
`printf '%s\\n' '<key>OpenClawGitCommit</key><string>${sourceCommit}</string>' >>"$app/Contents/Info.plist"`,
`printf '%s\\n' '<key>PeekabooSourceCommit</key><string>${peekabooCommit}</string>' >>"$app/Contents/Info.plist"`,
"printf '%s\\n' '<key>CFBundleShortVersionString</key><string>4.2.0</string>' '<key>CFBundleVersion</key><string>420</string>' '</dict></plist>' >>\"$app/Contents/Info.plist\"",
'plutil -insert OpenClawBuildTimestamp -string 2026-08-28T00:00:00Z "$app/Contents/Info.plist"',
'plutil -insert OpenClawWorkerBuildID -string fixture-build "$app/Contents/Info.plist"',
// Emit directly: Bash can block pre-filling a heredoc before its reader starts.
`builtin printf '%s\\n' ${quoteCliArg(
[
"#!/bin/sh",
'if [ "${1:-}" = "--elevation-sync-file" ] && [ "${TEST_KILL_AFTER_PENDING_RECEIPT:-0}" = "1" ] && [ ! -e "$TEST_PENDING_KILL_MARKER" ] && echo "${2:-}" | grep -q \'elevation-host-install[.]pending[.]json$\'; then',
' : >"$TEST_PENDING_KILL_MARKER"',
' kill -KILL "$PPID"',
"fi",
'if [ "${1:-}" = "--elevation-sync-file" ] && [ "${TEST_CORRUPT_ROLLBACK_PLIST_BACKUP_ON_SYNC:-0}" = "1" ] && echo "${2:-}" | grep -q \'elevation-host[.]previous-plist[.]\'; then',
" printf '%s\\n' corrupt >\"$2\"",
"fi",
'if [ "${1:-}" = "--elevation-rename-exclusive" ]; then',
' if [ "${TEST_FAIL_ELEVATION_QUARANTINE_RENAME:-0}" = "1" ] && echo "$3" | grep -q \'elevation-host[.]quarantined-launch-agent[.]\'; then',
" exit 7",
" fi",
' if [ "${TEST_DANGLING_ROLLBACK_DURING_MOVE:-0}" = "1" ] && echo "$3" | grep -q \'[.]rollback-elevation-host-\'; then',
' ln -s /missing/openclaw-rollback-target "$3"',
" fi",
' if [ "${TEST_SIGNAL_BEFORE_ROLLBACK_APP_MOVE:-0}" = "1" ] && echo "$3" | grep -q \'[.]rollback-elevation-host-\'; then',
' kill -TERM "$PPID"',
" exit 7",
" fi",
' if [ "${TEST_SYMLINK_MIGRATION_SOURCE_DURING_REVERSAL_CUSTODY:-0}" = "1" ] && echo "$3" | grep -q \'[.]reversal-custody[.]\'; then',
' rm -f "$2"',
' ln -s /replacement-owner "$2"',
" fi",
' if [ "${TEST_SYMLINK_DAMAGED_APP_BEFORE_CUSTODY:-0}" = "1" ] && echo "$3" | grep -q \'[.]failed-elevation-host-.*[/]OpenClaw[.]app$\'; then',
' rm -rf "$2"',
' ln -s /replacement-app "$2"',
" fi",
' if [ "${TEST_REPLACE_MIGRATION_SOURCE_SAME_CONTENT_BEFORE_CUSTODY:-0}" = "1" ] && echo "$3" | grep -q \'[.]reversal-custody[.]\'; then',
' /bin/mv "$2" "$2.race-displaced"',
' /bin/cp -p "$2.race-displaced" "$2"',
" fi",
' if [ "${TEST_REPLACE_DAMAGED_APP_DIRECTORY_BEFORE_CUSTODY:-0}" = "1" ] && echo "$3" | grep -q \'[.]failed-elevation-host-.*[/]OpenClaw[.]app$\'; then',
' /bin/mv "$2" "$2.race-displaced"',
' /bin/mkdir -p "$2/Contents/MacOS"',
" printf '%s\\n' replacement-directory >\"$2/Contents/replacement\"",
" fi",
' if [ "${TEST_RACE_MIGRATION_CUSTODY_DESTINATION:-0}" = "1" ] && echo "$3" | grep -q \'[.]custody[.]\'; then',
" printf '%s\\n' raced-custody-owner >\"$3\"",
" fi",
' if [ "${TEST_REPLACE_MIGRATION_SOURCE_SAME_CONTENT_BEFORE_INITIAL_CUSTODY:-0}" = "1" ] && echo "$3" | grep -q \'[.]custody[.]\'; then',
' /bin/mv "$2" "$2.race-displaced"',
' /bin/cp -p "$2.race-displaced" "$2"',
" fi",
' if [ -e "$3" ] || [ -L "$3" ]; then exit 1; fi',
' /bin/mv "$2" "$3" || exit $?',
' if [ "${TEST_REMOVE_CUA_DRIVER_AFTER_UNSAFE_ENTRY_MOVE:-0}" = "1" ] && echo "$3" | grep -q \'elevation-host[.]quarantined-app[.].*[/]OpenClaw[.]app$\' && [ -L "$3" ]; then',
' /bin/rm -f -- "$(readlink "$3")/Contents/Resources/cua-driver"',
" fi",
' if [ "${TEST_RELOAD_ELEVATION_AFTER_QUARANTINE:-0}" = "1" ] && echo "$3" | grep -q \'elevation-host[.]quarantined-launch-agent[.]\'; then',
" printf '%s\\n' elevation-loaded >\"$TEST_LAUNCH_STATE_FILE\"",
" fi",
' if [ "${TEST_KILL_AFTER_INITIAL_MIGRATION_CUSTODY:-0}" = "1" ] && echo "$3" | grep -q \'[.]custody[.]\'; then',
' kill -KILL "$PPID"',
" fi",
' if [ "${TEST_KILL_AFTER_ROLLBACK_APP_CUSTODY:-0}" = "1" ] && echo "$3" | grep -q \'[.]rollback-elevation-host-\'; then',
' kill -KILL "$PPID"',
" fi",
' if [ "${TEST_SIGNAL_DURING_CUSTODY:-0}" = "1" ] && echo "$3" | grep -q \'[.]custody[.]\'; then',
' kill -"$TEST_CUSTODY_SIGNAL" "$PPID"',
" fi",
' if [ "${TEST_REPLACE_MIGRATION_SOURCE_DURING_REVERSAL_CUSTODY:-0}" = "1" ] && echo "$3" | grep -q \'[.]reversal-custody[.]\'; then',
" printf '%s\\n' replacement-owner >\"$2\"",
" fi",
' if [ "${TEST_RECREATE_APP_DURING_DAMAGED_CUSTODY:-0}" = "1" ] && echo "$3" | grep -q \'[.]failed-elevation-host-.*[/]OpenClaw[.]app$\'; then',
' mkdir -p "$2/Contents/MacOS"',
" printf '%s\\n' replacement >\"$2/Contents/replacement\"",
" fi",
' if [ "${TEST_SIGNAL_DURING_RECOVERY_APP_MOVE:-0}" = "1" ] && echo "$3" | grep -q \'[.]failed-elevation-host-.*[/]OpenClaw[.]app$\'; then',
' kill -TERM "$PPID"',
" fi",
" exit 0",
"fi",
"exit 0",
].join("\n"),
)} >"$app/Contents/MacOS/OpenClaw"`,
'printf helper >"$app/Contents/MacOS/openclaw-mlx-tts"',
'chmod 755 "$app/Contents/MacOS/OpenClaw" "$app/Contents/MacOS/openclaw-mlx-tts"',
'case "${TEST_CUA_DRIVER_KIND:-none}" in',
' file) mkdir -p "$app/Contents/Resources"; printf driver >"$app/Contents/Resources/cua-driver"; chmod 755 "$app/Contents/Resources/cua-driver" ;;',
' symlink) mkdir -p "$app/Contents/Resources"; ln -s /missing/cua-driver "$app/Contents/Resources/cua-driver" ;;',
" none) ;;",
" *) exit 64 ;;",
"esac",
"",
].join("\n"),
);
writeCommandFixture(
binDir,
"codesign",
[
"#!/usr/bin/env bash",
"set -euo pipefail",
'target="${!#}"',
'if [[ "$*" == *"--verify"* && "$*" == *"--all-architectures"* && "${TEST_ROLLBACK_NON_NATIVE_SIGNATURE_INVALID:-0}" == "1" && -e "$target/Contents/old-fixture" ]]; then',
" exit 1",
"fi",
'if [[ "$*" == *"--verify"* && -d "$target" && ! -e "$target/Contents/MacOS/OpenClaw" ]]; then',
" exit 1",
"fi",
'if [[ "$*" == *"--verify"* && "${TEST_FINAL_SIGNATURE_INVALID:-0}" == "1" && "$target" == "${TEST_INSTALLED_APP_PATH:-}" && -f "${TEST_LAUNCH_STATE_FILE:-}" && "$(tr -d \'\\n\' <"$TEST_LAUNCH_STATE_FILE")" == "elevation-loaded" ]]; then',
" exit 1",
"fi",
'if [[ "$*" == *"--verify"* && "${TEST_CURRENT_CUA_SIGNATURE_INVALID:-0}" == "1" && "$target" == "${TEST_INSTALLED_APP_PATH:-}" && ( -e "$target/Contents/Resources/cua-driver" || -L "$target/Contents/Resources/cua-driver" ) ]]; then',
" exit 1",
"fi",
'if [[ "$*" == *"--verify"* && -e "$target/Contents/invalid-signature" ]]; then',
" exit 1",
"fi",
'if [[ "$*" == *"--entitlements"* ]]; then',
" printf '%s\\n' '<plist><dict/></plist>'",
" exit 0",
"fi",
'if [[ "$*" == *"-dv"* ]]; then',
" cdhash=FIXTURECDHASH",
' if [[ -e "$target/Contents/old-fixture" ]]; then',
" cdhash=OLDFIXTURECDHASH",
" fi",
' if [[ "$*" == *"--arch arm64"* ]]; then',
' cdhash="${cdhash}ARM64"',
' elif [[ "$*" == *"--arch x86_64"* ]]; then',
' cdhash="${cdhash}X8664"',
' elif [[ "${TEST_NATIVE_ARCH:-arm64}" == "x86_64" ]]; then',
' cdhash="${cdhash}X8664"',
" else",
' cdhash="${cdhash}ARM64"',
" fi",
' if [[ "${TEST_FINAL_CDHASH_MISMATCH:-0}" == "1" && "$target" == "${TEST_INSTALLED_APP_PATH:-}" && "$*" == *"--arch x86_64"* && -f "${TEST_LAUNCH_STATE_FILE:-}" && "$(tr -d \'\\n\' <"$TEST_LAUNCH_STATE_FILE")" == "elevation-loaded" ]]; then',
" cdhash=FINALMISMATCHX8664",
" fi",
" format='Mach-O universal (x86_64 arm64)'",
' [[ ! -d "$target" ]] || format="app bundle with Mach-O universal (x86_64 arm64)"',
" printf 'Format=%s\\nCodeDirectory v=20400\\n' \"$format\" >&2",
" printf '%s\\n' 'Authority=Developer ID Application: OpenClaw Foundation (FWJYW4S8P8)' >&2",
" printf '%s\\n' 'TeamIdentifier=FWJYW4S8P8' >&2",
" printf 'CDHash=%s\\n' \"$cdhash\" >&2",
"fi",
"exit 0",
"",
].join("\n"),
);
writeArtifactFileFixture(binDir);
writeLipoFixture(binDir);
writeCommandFixture(binDir, "spctl", "#!/bin/sh\nexit 0\n");
writeCommandFixture(binDir, "xcrun", "#!/bin/sh\nexit 0\n");
const receipt = {
schemaVersion: 1,
kind: "openclaw-elevation-artifact",
archive: path.basename(archivePath),
archiveSha256: sha256(readFileSync(archivePath)),
archiveChecksum: `${path.basename(archivePath)}.sha256`,
installer: path.basename(installerPath),
installerSha256: sha256(readFileSync(installerPath)),
installerChecksum: `${path.basename(installerPath)}.sha256`,
sourceCommit,
peekabooCommit,
version: "4.2.0",
build: "420",
authority: "Developer ID Application: OpenClaw Foundation (FWJYW4S8P8)",
teamIdentifier: "FWJYW4S8P8",
cdhashes: { arm64: "FIXTURECDHASHARM64", x86_64: "FIXTURECDHASHX8664" },
architectures: { main: "x86_64 arm64", helper: "x86_64 arm64" },
entitlementsSha256: { main: sha256(entitlements), helper: sha256(entitlements) },
notarizationId: "12345678-1234-1234-1234-123456789abc",
};
writeFileSync(receiptPath, JSON.stringify(receipt), "utf8");
return {
archivePath,
dittoMarker,
installerPath,
peekabooCommit,
receipt,
receiptPath,
sourceCommit,
env: {
...process.env,
BASH_ENV: commandFixturesPath(binDir),
HOME: tempRoot,
PATH: `${binDir}${path.delimiter}${process.env.PATH ?? ""}`,
TEST_DITTO_MARKER: dittoMarker,
TEST_ARTIFACT_WORKERS: workers,
TEST_FIXTURE_ROOT: tempRoot,
TMPDIR: tempRoot,
},
};
}
function createInstallRollbackHarness(
mac: MacScriptFixture,
options: {
danglingRollbackDuringMove?: boolean;
corruptRollbackPlistBackupOnSync?: boolean;
currentCuaSignatureInvalid?: boolean;
failCurrentReceiptRestoreCopy?: boolean;
failAfterReceiptCommitMove?: boolean;
failRecoveryXattrRead?: boolean;
finalCDHashMismatch?: boolean;
finalSignatureInvalid?: boolean;
failLsofInspection?: boolean;
failPgrepInspection?: boolean;
failUnsafeEntryIdentity?: boolean;
failUnsafeEntryMktemp?: boolean;
hupDuringCustody?: boolean;
launchdBootstrapFails?: boolean;
failElevationQuarantineRename?: boolean;
killDuringMigrationRestoreBootstrapOnce?: boolean;
killAfterMigrationRestoreBootstrapOnce?: boolean;
killAfterInitialMigrationCustody?: boolean;
killAfterPendingReceipt?: boolean;
killAfterRollbackAppCustody?: boolean;
migrationRestoreBootstrapFails?: boolean;
raceMigrationCustodyDestination?: boolean;
reloadElevationAfterQuarantine?: boolean;
removeInstalledExecutableAfterReadiness?: boolean;
existingElevationLoaded?: boolean;
recreateAppDuringDamagedCustody?: boolean;
recreateSourceDuringBootout?: boolean;
recreateSourceOnFailure?: boolean;
replaceAuthenticatedRenameHelperBeforeUse?: boolean;
replaceDamagedAppDirectoryBeforeCustody?: boolean;
replaceMigrationSourceDuringReversalCustody?: boolean;
replaceMigrationSourceSameContentBeforeCustody?: boolean;
replaceMigrationSourceSameContentBeforeInitialCustody?: boolean;
restartAppDuringBootout?: boolean;
rollbackNonNativeSignatureInvalid?: boolean;
rollbackCuaDriverKind?: "file" | "symlink";
removeCuaDriverAfterUnsafeEntryMove?: boolean;
signalDuringCustody?: boolean;
signalDuringRecoveryAppMove?: boolean;
signalDuringReceiptCommit?: boolean;
signalBeforeRollbackAppMove?: boolean;
symlinkMigrationSourceDuringReversalCustody?: boolean;
symlinkDamagedAppBeforeCustody?: boolean;
sameSourceExistingApp?: boolean;
transientAppRestartReloadsJob?: boolean;
unsafeEntryEvidence?:
| "job"
| "plist"
| "plist-program"
| "receipt"
| "unrelated-plist"
| "unrelated-program"
| "unrelated-receipt";
} = {},
) {
const artifact = createArtifactVerificationHarness(mac);
// macOS Bash subshell functions report the terminating signal inside an EXIT trap.
// Keep real commands for cooperative signal cleanup; SIGKILL has no EXIT cleanup.
const exercisesSignalCleanup = Boolean(
options.hupDuringCustody ||
options.signalDuringCustody ||
options.signalDuringRecoveryAppMove ||
options.signalDuringReceiptCommit ||
options.signalBeforeRollbackAppMove,
);
const tempRoot = artifact.env.HOME;
const binDir = path.join(tempRoot, "bin");
writeDiskutilFixture(binDir);
writeDfFixture(binDir);
const stateDir = path.join(tempRoot, "node-state");
const configPath = path.join(stateDir, "openclaw.json");
const appPath = path.join(tempRoot, "InstalledOpenClaw.app");
const oldSourceCommit = options.sameSourceExistingApp ? artifact.sourceCommit : "c".repeat(40);
const oldPeekabooCommit = "d".repeat(40);
const label = "ai.openclaw.mac.node-fixture";
const launchAgentsDir = path.join(tempRoot, "Library", "LaunchAgents");
const sourcePlist = path.join(launchAgentsDir, `${label}.plist`);
const elevationPlist = path.join(launchAgentsDir, "ai.openclaw.mac.elevation-host.plist");
const launchStateFile = path.join(tempRoot, "launch-state");
const nodeGenerationFile = path.join(tempRoot, "node-generation");
mkdirSync(path.join(stateDir, "state"), { recursive: true });
mkdirSync(launchAgentsDir, { recursive: true });
writeFileSync(configPath, "{}\n", "utf8");
writeFileSync(path.join(stateDir, "state", "openclaw.sqlite"), "fixture", "utf8");
writeAppInfoPlist(appPath, oldSourceCommit, oldPeekabooCommit);
writeExecutable(path.join(appPath, "Contents", "MacOS", "OpenClaw"), "#!/bin/sh\nexit 0\n");
writeFileSync(path.join(appPath, "Contents", "old-fixture"), "old\n", "utf8");
if (options.rollbackCuaDriverKind) {
const resources = path.join(appPath, "Contents", "Resources");
const cuaDriver = path.join(resources, "cua-driver");
mkdirSync(resources, { recursive: true });
if (options.rollbackCuaDriverKind === "file") {
writeExecutable(cuaDriver, "#!/bin/sh\nexit 0\n");
} else {
symlinkSync("/missing/cua-driver", cuaDriver);
}
}
const sourceContents = [
'<?xml version="1.0" encoding="UTF-8"?>',
'<plist version="1.0"><dict>',
`<key>Label</key><string>${label}</string>`,
"<key>ProgramArguments</key><array>",
`<string>${appPath}/Contents/MacOS/OpenClaw</string>`,
"<string>--attach-only</string><string>--background-only</string>",
"</array>",
"<key>EnvironmentVariables</key><dict>",
`<key>OPENCLAW_STATE_DIR</key><string>${stateDir}</string>`,
`<key>OPENCLAW_CONFIG_PATH</key><string>${configPath}</string>`,
"</dict></dict></plist>",
"",
].join("\n");
const elevationPlistContents = [
'<?xml version="1.0" encoding="UTF-8"?>',
'<plist version="1.0"><dict>',
"<key>Label</key><string>ai.openclaw.mac.elevation-host</string>",
`<key>ProgramArguments</key><array><string>${appPath}/Contents/MacOS/OpenClaw</string><string>--elevation-host</string></array>`,
"<key>EnvironmentVariables</key><dict>",
`<key>OPENCLAW_STATE_DIR</key><string>${stateDir}</string>`,
`<key>OPENCLAW_CONFIG_PATH</key><string>${configPath}</string>`,
"</dict></dict></plist>",
"",
].join("\n");
if (options.existingElevationLoaded) {
writeFileSync(elevationPlist, elevationPlistContents, "utf8");
writeFileSync(launchStateFile, "elevation-loaded\n", "utf8");
} else {
writeFileSync(sourcePlist, sourceContents, "utf8");
writeFileSync(launchStateFile, "source-loaded\n", "utf8");
}
writeFileSync(nodeGenerationFile, "0\n", "utf8");
writeExecutable(path.join(binDir, "defaults"), "#!/bin/sh\nprintf '%s\\n' primary\n");
writeCommandFixture(
binDir,
"mktemp",
[
"#!/usr/bin/env bash",
'if [[ "${TEST_FAIL_UNSAFE_ENTRY_MKTEMP:-0}" == "1" && "$*" == *"elevation-host.quarantined-app."* ]]; then',
" exit 7",
"fi",
'exec /usr/bin/mktemp "$@"',
"",
].join("\n"),
);
if (options.replaceAuthenticatedRenameHelperBeforeUse) {
writeCommandFixture(
binDir,
"shasum",
[
"#!/usr/bin/env bash",
"set -euo pipefail",
'target="${!#}"',
'if [[ "$target" == */openclaw-elevation.*/OpenClaw.app/Contents/MacOS/OpenClaw ]]; then',
' if [[ -e "$TEST_RENAME_HELPER_HASH_MARKER" ]]; then',
" printf '%s\\n' '#!/bin/sh' 'exit 0' >\"$target\"",
' chmod 755 "$target"',
" else",
' : >"$TEST_RENAME_HELPER_HASH_MARKER"',
" fi",
"fi",
'exec /usr/bin/shasum "$@"',
"",
].join("\n"),
);
}
writeCommandFixture(binDir, "sqlite3", "#!/bin/sh\nprintf '%s\\n' fixture-node\n");
writeCommandFixture(
binDir,
"pgrep",
[
"#!/usr/bin/env bash",
"set -euo pipefail",
'state="$(tr -d \'\\n\' <"$TEST_LAUNCH_STATE_FILE")"',
'if [[ "$TEST_FAIL_PGREP_INSPECTION" == "1" && "$state" == "source-absent" ]]; then',
" exit 7",
"fi",
'if [[ "$TEST_FAIL_LSOF_INSPECTION" == "1" && "$state" == "source-absent" ]]; then',
" printf '%s\\n' \"$TEST_LIVE_PID\"",
" exit 0",
"fi",
'if [[ "$TEST_TRANSIENT_APP_RESTART_RELOADS_JOB" == "1" && "$state" == "source-absent" ]]; then',
" printf '%s\\n' source-loaded >\"$TEST_LAUNCH_STATE_FILE\"",
" printf '%s\\n' 777777",
" exit 0",
"fi",
'if [[ "$TEST_RESTART_APP_DURING_BOOTOUT" == "1" && "$state" == "source-absent" ]]; then',
" printf '%s\\n' 777777",
" exit 0",
"fi",
"exit 1",
"",
].join("\n"),
);
writeExecutable(
path.join(binDir, "lsof"),
[
"#!/usr/bin/env bash",
"set -euo pipefail",
'[[ "$TEST_FAIL_LSOF_INSPECTION" != "1" ]] || exit 7',
`printf '%s\\n' p777777 n${JSON.stringify(path.join(appPath, "Contents", "MacOS", "OpenClaw"))}`,
"",
].join("\n"),
);
writeCommandFixture(binDir, "sleep", "#!/bin/sh\nexit 0\n");
writeCommandFixture(
binDir,
"mv",
[
"#!/usr/bin/env bash",
"set -euo pipefail",
'destination="${!#}"',
'if [[ "$TEST_SIGNAL_DURING_CUSTODY" == "1" && "$destination" == *.custody.* ]]; then',
' /bin/mv "$@"',
' kill -"$TEST_CUSTODY_SIGNAL" "$PPID"',
" exit 0",
"fi",
'if [[ "$TEST_SIGNAL_DURING_RECEIPT_COMMIT" == "1" && "$destination" == */elevation-host-install.json ]]; then',
' /bin/mv "$@"',
' kill -TERM "$PPID"',
" exit 0",
"fi",
'if [[ "$TEST_FAIL_AFTER_RECEIPT_COMMIT_MOVE" == "1" && "$destination" == */elevation-host-install.json ]]; then',
' /bin/mv "$@"',
" exit 7",
"fi",
'if [[ "$TEST_SIGNAL_DURING_RECOVERY_APP_MOVE" == "1" && "$destination" == *.failed-elevation-host-*/OpenClaw.app ]]; then',
' /bin/mv "$@"',
' kill -TERM "$PPID"',
" exit 0",
"fi",
'if [[ "$TEST_SIGNAL_BEFORE_ROLLBACK_APP_MOVE" == "1" && "$destination" == *.rollback-elevation-host-* ]]; then',
' kill -TERM "$PPID"',
" exit 7",
"fi",
'exec /bin/mv "$@"',
"",
].join("\n"),
);
writeCommandFixture(
binDir,
"cp",
[
"#!/usr/bin/env bash",
"set -euo pipefail",
'destination="${!#}"',
'if [[ "$TEST_FAIL_CURRENT_RECEIPT_RESTORE_COPY" == "1" && "$destination" == *elevation-host-install.json.restore.* ]]; then',
" printf '%s\\n' partial >\"$destination\"",
" exit 7",
"fi",
'exec /bin/cp "$@"',
"",
].join("\n"),
);
writeExecutable(
path.join(binDir, "openclaw"),
[
"#!/usr/bin/env bash",
"set -euo pipefail",
'case "$*" in',
" *'config get gateway.mode'*) printf '%s\\n' '\"remote\"' ;;",
" *'config get gateway.remote.url'*) printf '%s\\n' '\"wss://gateway.invalid\"' ;;",
" *'config get gateway.remote.token'*) printf '%s\\n' '\"redacted\"' ;;",
" *'config get gateway.remote.password'*) exit 1 ;;",
" *'nodes status'*)",
' state="$(tr -d \'\\n\' <"$TEST_LAUNCH_STATE_FILE")"',
' if [[ "$state" == "elevation-loaded" ]]; then',
' generation="$(tr -d \'\\n\' <"$TEST_NODE_GENERATION_FILE")"',
' connected_at="$((10 + generation * 10))"',
' printf \'{"nodes":[{"nodeId":"fixture-node","paired":true,"connected":true,"connectedAtMs":%s,"clientId":"openclaw-macos","clientMode":"node","uiVersion":"4.2.0","caps":["computer"],"commands":["screen.snapshot","computer.act"],"computerUse":{"version":2}}]}\\n\' "$connected_at"',
" else",
' printf \'%s\\n\' \'{"nodes":[{"nodeId":"fixture-node","paired":true,"connected":true,"connectedAtMs":10}]}\'',
" fi ;;",
" *) exit 2 ;;",
"esac",
"",
].join("\n"),
);
writeCommandFixture(
binDir,
"launchctl",
[
"#!/usr/bin/env bash",
"set -euo pipefail",
'command_name="${1:-}"',
'target="${2:-}"',
'state="$(tr -d \'\\n\' <"$TEST_LAUNCH_STATE_FILE")"',
'if [[ "$command_name" == "print" ]]; then',
' if [[ "$target" == */ai.openclaw.mac.node-fixture && "$state" == "source-loaded" ]]; then',
" printf '%s\\n' ' pid = 999999'",
" exit 0",
" fi",
' if [[ "$target" == */ai.openclaw.mac.elevation-host && "$state" == "elevation-loaded" ]]; then',
" printf '%s\\n' ' pid = 555555'",
" printf ' program = %s/Contents/MacOS/OpenClaw\\n' \"$TEST_INSTALLED_APP_PATH\"",
" printf '%s\\n' ' arguments = {' ' --elevation-host' ' }'",
" exit 0",
" fi",
" printf '%s\\n' 'Could not find service in domain' >&2",
" exit 113",
"fi",
'if [[ "$command_name" == "bootout" && "$target" == */ai.openclaw.mac.node-fixture ]]; then',
" printf '%s\\n' source-absent >\"$TEST_LAUNCH_STATE_FILE\"",
' if [[ "$TEST_RECREATE_SOURCE_DURING_BOOTOUT" == "1" ]]; then',
" printf '%s\\n' replacement-owner >\"$TEST_SOURCE_PLIST\"",
" fi",
" exit 0",
"fi",
'if [[ "$command_name" == "bootout" && "$target" == */ai.openclaw.mac.elevation-host ]]; then',
" printf '%s\\n' elevation-absent >\"$TEST_LAUNCH_STATE_FILE\"",
" exit 0",
"fi",
'if [[ "$command_name" == "bootstrap" ]]; then',
' plist="${3:-}"',
' if [[ "$plist" == *ai.openclaw.mac.elevation-host.plist ]]; then',
' if [[ "$TEST_LAUNCHD_BOOTSTRAP_FAILS" == "1" ]]; then',
' if [[ -n "$TEST_UNSAFE_ENTRY_EVIDENCE" ]]; then',
' rollback_app="$(find "$(dirname "$TEST_INSTALLED_APP_PATH")" -maxdepth 1 -type d -name "$(basename "$TEST_INSTALLED_APP_PATH").rollback-elevation-host-*" -print -quit)"',
' [[ -n "$rollback_app" ]] || exit 71',
' /bin/rm -rf -- "$TEST_INSTALLED_APP_PATH"',
' if [[ "$TEST_UNSAFE_ENTRY_EVIDENCE" == unrelated-* ]]; then',
' /bin/mv "$rollback_app" "$TEST_INSTALLED_APP_PATH"',
" else",
' ln -s "$rollback_app" "$TEST_INSTALLED_APP_PATH"',
" fi",
' pending_receipt="$TEST_STATE_DIR/elevation-host-install.pending.json"',
' case "$TEST_UNSAFE_ENTRY_EVIDENCE" in',
' job) /bin/rm -f -- "$TEST_ELEVATION_PLIST" "$pending_receipt"; printf \'%s\\n\' elevation-loaded >"$TEST_LAUNCH_STATE_FILE" ;;',
' plist) /bin/rm -f -- "$pending_receipt"; printf \'%s\\n\' elevation-absent >"$TEST_LAUNCH_STATE_FILE" ;;',
' plist-program) /bin/rm -f -- "$pending_receipt"; /usr/bin/plutil -insert Program -string "$TEST_INSTALLED_APP_PATH/Contents/MacOS/OpenClaw" "$TEST_ELEVATION_PLIST"; printf \'%s\\n\' elevation-absent >"$TEST_LAUNCH_STATE_FILE" ;;',
' receipt) /bin/rm -f -- "$TEST_ELEVATION_PLIST"; printf \'%s\\n\' elevation-absent >"$TEST_LAUNCH_STATE_FILE" ;;',
' unrelated-plist) /bin/rm -f -- "$pending_receipt"; /usr/bin/plutil -replace ProgramArguments.0 -string "$TEST_UNRELATED_APP_PATH/Contents/MacOS/OpenClaw" "$TEST_ELEVATION_PLIST"; printf \'%s\\n\' elevation-absent >"$TEST_LAUNCH_STATE_FILE" ;;',
' unrelated-program) /bin/rm -f -- "$pending_receipt"; /usr/bin/plutil -insert Program -string "$TEST_UNRELATED_APP_PATH/Contents/MacOS/OpenClaw" "$TEST_ELEVATION_PLIST"; printf \'%s\\n\' elevation-absent >"$TEST_LAUNCH_STATE_FILE" ;;',
' unrelated-receipt) /bin/rm -f -- "$TEST_ELEVATION_PLIST"; jq --arg appPath "$TEST_UNRELATED_APP_PATH" \'.appPath = $appPath\' "$pending_receipt" >"$pending_receipt.tmp"; /bin/mv "$pending_receipt.tmp" "$TEST_STATE_DIR/elevation-host-install.json"; /bin/rm -f -- "$pending_receipt"; printf \'%s\\n\' elevation-absent >"$TEST_LAUNCH_STATE_FILE" ;;',
" *) exit 72 ;;",
" esac",
" fi",
' if [[ "$TEST_RECREATE_SOURCE_ON_FAILURE" == "1" ]]; then',
" printf '%s\\n' replacement-owner >\"$TEST_SOURCE_PLIST\"",
" fi",
" exit 7",
" fi",
' generation="$(tr -d \'\\n\' <"$TEST_NODE_GENERATION_FILE")"',
' printf \'%s\\n\' "$((generation + 1))" >"$TEST_NODE_GENERATION_FILE"',
" printf '%s\\n' elevation-loaded >\"$TEST_LAUNCH_STATE_FILE\"",
" exit 0",
" fi",
' if [[ "$plist" == *ai.openclaw.mac.node-fixture.plist ]]; then',
' if [[ "$TEST_KILL_AFTER_MIGRATION_RESTORE_BOOTSTRAP_ONCE" == "1" && ! -e "$TEST_RECOVERY_KILL_MARKER" ]]; then',
' : >"$TEST_RECOVERY_KILL_MARKER"',
" printf '%s\\n' source-loaded >\"$TEST_LAUNCH_STATE_FILE\"",
' kill -KILL "$PPID"',
" exit 137",
" fi",
' if [[ "$TEST_KILL_DURING_MIGRATION_RESTORE_BOOTSTRAP_ONCE" == "1" && ! -e "$TEST_RECOVERY_KILL_MARKER" ]]; then',
' : >"$TEST_RECOVERY_KILL_MARKER"',
' kill -KILL "$PPID"',
" exit 137",
" fi",
' if [[ "$TEST_MIGRATION_RESTORE_BOOTSTRAP_FAILS" == "1" ]]; then',
" exit 9",
" fi",
" printf '%s\\n' source-loaded >\"$TEST_LAUNCH_STATE_FILE\"",
" exit 0",
" fi",
"fi",
"exit 0",
"",
].join("\n"),
// This shim sends SIGKILL to its actual installer parent.
Boolean(
options.killDuringMigrationRestoreBootstrapOnce ||
options.killAfterMigrationRestoreBootstrapOnce,
),
);
writeCommandFixture(
binDir,
"peekaboo",
[
"#!/usr/bin/env bash",
"set -euo pipefail",
'if [[ "${1:-}" == "bridge" ]]; then',
' if [[ "${TEST_REMOVE_INSTALLED_EXECUTABLE_AFTER_READINESS:-0}" == "1" ]]; then',
' rm -f "$TEST_INSTALLED_APP_PATH/Contents/MacOS/OpenClaw"',
" fi",
' printf \'%s\\n\' \'{"success":true,"data":{"selected":{"handshake":{"hostIdentity":{"processIdentifier":555555}}}}}\'',
" exit 0",
"fi",
'if [[ "${1:-}" == "permissions" ]]; then',
' printf \'%s\\n\' \'{"success":true,"data":{"sources":[{"isSelected":true,"permissions":[{"name":"Screen Recording","isGranted":true}]}]}}\'',
" exit 0",
"fi",
"exit 2",
"",
].join("\n"),
);
writeCommandFixture(
binDir,
"xattr",
[
"#!/bin/sh",
'if [ "${TEST_FAIL_RECOVERY_XATTR_READ:-0}" = "1" ] && [ "${1:-}" = "-p" ]; then',
" exit 74",
"fi",
'exec /usr/bin/xattr "$@"',
"",
].join("\n"),
);
return {
...artifact,
appPath,
configPath,
elevationPlist,
elevationPlistContents,
label,
launchStateFile,
sourceContents,
sourcePlist,
stateDir,
env: {
...artifact.env,
BASH_ENV: exercisesSignalCleanup ? "" : artifact.env.BASH_ENV,
TEST_DANGLING_ROLLBACK_DURING_MOVE: options.danglingRollbackDuringMove ? "1" : "0",
TEST_CORRUPT_ROLLBACK_PLIST_BACKUP_ON_SYNC: options.corruptRollbackPlistBackupOnSync
? "1"
: "0",
TEST_FAIL_CURRENT_RECEIPT_RESTORE_COPY: options.failCurrentReceiptRestoreCopy ? "1" : "0",
TEST_FAIL_AFTER_RECEIPT_COMMIT_MOVE: options.failAfterReceiptCommitMove ? "1" : "0",
TEST_FAIL_ELEVATION_QUARANTINE_RENAME: options.failElevationQuarantineRename ? "1" : "0",
TEST_FAIL_LSOF_INSPECTION: options.failLsofInspection ? "1" : "0",
TEST_FAIL_PGREP_INSPECTION: options.failPgrepInspection ? "1" : "0",
TEST_FAIL_RECOVERY_XATTR_READ: options.failRecoveryXattrRead ? "1" : "0",
TEST_FAIL_UNSAFE_ENTRY_IDENTITY: options.failUnsafeEntryIdentity ? "1" : "0",
TEST_FAIL_UNSAFE_ENTRY_MKTEMP: options.failUnsafeEntryMktemp ? "1" : "0",
TEST_FINAL_CDHASH_MISMATCH: options.finalCDHashMismatch ? "1" : "0",
TEST_FINAL_SIGNATURE_INVALID: options.finalSignatureInvalid ? "1" : "0",
TEST_FIXTURE_ROOT: tempRoot,
TEST_INSTALLED_APP_PATH: appPath,
TEST_ELEVATION_PLIST: elevationPlist,
TEST_STATE_DIR: stateDir,
TEST_UNRELATED_APP_PATH: path.join(tempRoot, "UnrelatedOpenClaw.app"),
TEST_UNSAFE_ENTRY_EVIDENCE: options.unsafeEntryEvidence ?? "",
TEST_CUSTODY_SIGNAL: options.hupDuringCustody ? "HUP" : "TERM",
TEST_CURRENT_CUA_SIGNATURE_INVALID: options.currentCuaSignatureInvalid ? "1" : "0",
TEST_LAUNCHD_BOOTSTRAP_FAILS: options.launchdBootstrapFails === false ? "0" : "1",
TEST_LIVE_PID: String(process.pid),
TEST_LAUNCH_STATE_FILE: launchStateFile,
TEST_KILL_DURING_MIGRATION_RESTORE_BOOTSTRAP_ONCE:
options.killDuringMigrationRestoreBootstrapOnce ? "1" : "0",
TEST_KILL_AFTER_MIGRATION_RESTORE_BOOTSTRAP_ONCE:
options.killAfterMigrationRestoreBootstrapOnce ? "1" : "0",
TEST_KILL_AFTER_INITIAL_MIGRATION_CUSTODY: options.killAfterInitialMigrationCustody
? "1"
: "0",
TEST_KILL_AFTER_PENDING_RECEIPT: options.killAfterPendingReceipt ? "1" : "0",
TEST_PENDING_KILL_MARKER: path.join(tempRoot, "pending-kill-marker"),
TEST_KILL_AFTER_ROLLBACK_APP_CUSTODY: options.killAfterRollbackAppCustody ? "1" : "0",
TEST_NODE_GENERATION_FILE: nodeGenerationFile,
TEST_RECOVERY_KILL_MARKER: path.join(tempRoot, "recovery-kill-marker"),
TEST_MIGRATION_RESTORE_BOOTSTRAP_FAILS: options.migrationRestoreBootstrapFails ? "1" : "0",
TEST_RACE_MIGRATION_CUSTODY_DESTINATION: options.raceMigrationCustodyDestination ? "1" : "0",
TEST_RECREATE_APP_DURING_DAMAGED_CUSTODY: options.recreateAppDuringDamagedCustody ? "1" : "0",
TEST_RECREATE_SOURCE_DURING_BOOTOUT: options.recreateSourceDuringBootout ? "1" : "0",
TEST_RECREATE_SOURCE_ON_FAILURE: options.recreateSourceOnFailure ? "1" : "0",
TEST_RELOAD_ELEVATION_AFTER_QUARANTINE: options.reloadElevationAfterQuarantine ? "1" : "0",
TEST_RENAME_HELPER_HASH_MARKER: path.join(tempRoot, "rename-helper-hash-marker"),
TEST_REPLACE_DAMAGED_APP_DIRECTORY_BEFORE_CUSTODY:
options.replaceDamagedAppDirectoryBeforeCustody ? "1" : "0",
TEST_REPLACE_MIGRATION_SOURCE_DURING_REVERSAL_CUSTODY:
options.replaceMigrationSourceDuringReversalCustody ? "1" : "0",
TEST_REPLACE_MIGRATION_SOURCE_SAME_CONTENT_BEFORE_CUSTODY:
options.replaceMigrationSourceSameContentBeforeCustody ? "1" : "0",
TEST_REPLACE_MIGRATION_SOURCE_SAME_CONTENT_BEFORE_INITIAL_CUSTODY:
options.replaceMigrationSourceSameContentBeforeInitialCustody ? "1" : "0",
TEST_REMOVE_INSTALLED_EXECUTABLE_AFTER_READINESS:
options.removeInstalledExecutableAfterReadiness ? "1" : "0",
TEST_REMOVE_CUA_DRIVER_AFTER_UNSAFE_ENTRY_MOVE: options.removeCuaDriverAfterUnsafeEntryMove
? "1"
: "0",
TEST_RESTART_APP_DURING_BOOTOUT: options.restartAppDuringBootout ? "1" : "0",
TEST_ROLLBACK_NON_NATIVE_SIGNATURE_INVALID: options.rollbackNonNativeSignatureInvalid
? "1"
: "0",
TEST_SIGNAL_DURING_CUSTODY:
options.signalDuringCustody || options.hupDuringCustody ? "1" : "0",
TEST_SIGNAL_DURING_RECOVERY_APP_MOVE: options.signalDuringRecoveryAppMove ? "1" : "0",
TEST_SIGNAL_DURING_RECEIPT_COMMIT: options.signalDuringReceiptCommit ? "1" : "0",
TEST_SIGNAL_BEFORE_ROLLBACK_APP_MOVE: options.signalBeforeRollbackAppMove ? "1" : "0",
TEST_SYMLINK_MIGRATION_SOURCE_DURING_REVERSAL_CUSTODY:
options.symlinkMigrationSourceDuringReversalCustody ? "1" : "0",
TEST_SYMLINK_DAMAGED_APP_BEFORE_CUSTODY: options.symlinkDamagedAppBeforeCustody ? "1" : "0",
TEST_TRANSIENT_APP_RESTART_RELOADS_JOB: options.transientAppRestartReloadsJob ? "1" : "0",
TEST_SOURCE_PLIST: sourcePlist,
},
};
}
function createCodesignMetadataProbe(mac: MacScriptFixture) {
const root = mac.createTempDir("openclaw-elevation-metadata-probe-");
const source = readFileSync(scriptPath, "utf8");
const helpers = source.slice(
source.indexOf("codesign_metadata_value() {"),
source.indexOf("entitlements_for() {"),
);
const probe = path.join(root, "probe.bash");
const metadata = path.join(root, "metadata");
writeFileSync(
probe,
`set -euo pipefail\n${helpers}\nif [[ -f "$HOME/metadata" ]]; then codesign() { cat "$HOME/metadata"; }; fi\ncodesign_value_for_arch "$1" "$2" "$3"\n`,
);
return {
root,
metadata,
async run(target: string, key: string, arch: string) {
return await mac.run("/bin/bash", [probe, target, key, arch], {
encoding: "utf8",
env: { HOME: root, TMPDIR: root, PATH: "/usr/bin:/bin" },
timeout: 20_000,
});
},
};
}
describe("mac elevation host command contract", () => {
it.skipIf(process.platform !== "darwin").concurrent(
"reads genuine codesign metadata despite filename newlines using real Apple-signed bytes",
async ({ mac }) =>
mac.lifetime.run(async () => {
const probe = createCodesignMetadataProbe(mac);
const bytes = readFileSync("/usr/bin/true");
const control = path.join(probe.root, "control");
const injected = path.join(
probe.root,
"injected\nFormat=Mach-O thin (fake)\nCodeDirectory v=20400\nAuthority=Fake\nTeamIdentifier=Fake\nCDHash=Fake\n",
);
writeFileSync(control, bytes);
writeFileSync(injected, bytes);
const architectures = await mac.run("/usr/bin/lipo", ["-archs", control], {
encoding: "utf8",
timeout: 20_000,
});
expect(architectures.status, architectures.stderr).toBe(0);
for (const arch of architectures.stdout.trim().split(/\s+/)) {
for (const key of ["Format", "Authority", "TeamIdentifier", "CDHash"]) {
const normal = await probe.run(control, key, arch);
const renamed = await probe.run(injected, key, arch);
expect(normal.status, normal.stderr).toBe(0);
expect(normal.stdout.trim(), `${arch} ${key}`).not.toBe("");
expect(renamed.status, renamed.stderr).toBe(0);
expect(renamed.stdout, `${arch} ${key}`).toBe(normal.stdout);
}
}
expect(readFileSync(control)).toEqual(bytes);
expect(readFileSync(injected)).toEqual(bytes);
}),
);
it.concurrent.for(
["arm64", "x86_64"].flatMap((arch) =>
(["Authority", "TeamIdentifier", "CDHash", "Format"] as const).flatMap((key) =>
["present", "bundle", "missing", "generic", "missing-directory"].map((kind) => ({
arch,
key,
kind,
})),
),
),
)("reads scoped codesign metadata $arch $key ($kind)", async ({ arch, key, kind }, { mac }) =>
mac.lifetime.run(async () => {
const probe = createCodesignMetadataProbe(mac);
const fields = {
Format: `${kind === "bundle" ? "app bundle with " : ""}Mach-O thin (${arch})`,
Authority: "Real=Leaf",
TeamIdentifier: "REALTEAM",
CDHash: `REAL-${arch}`,
};
const genuine = [
"Identifier=real",
...(kind === "missing" && key === "Format"
? []
: [`Format=${kind === "generic" ? "generic" : fields.Format}`]),
...(kind === "missing-directory"
? []
: ["CodeDirectory v=20400 size=231 flags=0x0(none) hashes=2+2 location=embedded"]),
...(["Authority", "TeamIdentifier", "CDHash"] as const).flatMap((name) =>
kind === "missing" && key === name ? [] : [`${name}=${fields[name]}`],
),
...(key === "Authority" && kind === "missing" ? [] : ["Authority=Issuer"]),
];
writeFileSync(
probe.metadata,
[
"Executable=filename\nFormat=Mach-O thin (fake)\nCodeDirectory v=20400",
"Authority=Injected\nTeamIdentifier=Injected\nCDHash=Injected",
...genuine,
"",
].join("\n"),
);
const result = await probe.run("unused", key, arch);
expect(result.error).toBeUndefined();
if (kind === "present" || kind === "bundle") {
expect(result.status, result.stderr).toBe(0);
expect(result.stdout.trim()).toBe(fields[key]);
} else {
expect(result.status).toBe(1);
expect(result.stdout).toBe("");
}
}),
);
it.concurrent("reads complete codesign metadata without SIGPIPE and preserves codesign failure", async ({
mac,
}) =>
mac.lifetime.run(async () => {
const root = mac.createTempDir("openclaw-elevation-codesign-metadata-");
const binDir = path.join(root, "bin");
const fakeApp = path.join(root, "OpenClaw.app");
mkdirSync(binDir);
mkdirSync(fakeApp);
const codesign = path.join(binDir, "codesign");
writeExecutable(
codesign,
[
"#!/usr/bin/env bash",
"set -euo pipefail",
"printf '%s\\n' 'Format=app bundle with Mach-O universal (x86_64 arm64)' 'CodeDirectory v=20400' >&2",
"printf '%s\\n' 'Authority=Developer ID Application: OpenClaw Foundation (FWJYW4S8P8)' >&2",
"printf '%s\\n' 'Authority=Developer ID Certification Authority' >&2",
"printf '%s\\n' 'TeamIdentifier=FWJYW4S8P8' >&2",
'if [[ "$*" == *"--arch arm64"* ]]; then',
" printf '%s\\n' 'CDHash=ARM64HASH' >&2",
'elif [[ "$*" == *"--arch x86_64"* ]]; then',
" printf '%s\\n' 'CDHash=X8664HASH' >&2",
"else",
" printf '%s\\n' 'CDHash=UNSCOPEDHASH' >&2",
"fi",
"for _ in $(seq 1 10000); do printf '%s\\n' 'Padding=0123456789abcdef' >&2; done",
'[[ "${CODESIGN_FAIL_AFTER_OUTPUT:-0}" != "1" ]] || exit 7',
"",
].join("\n"),
);
const script = readFileSync(scriptPath, "utf8");
const start = script.indexOf("codesign_metadata_value() {");
const end = script.indexOf("entitlements_for()", start);
const helpers = script.slice(start, end);
const run = async (command: string, failAfterOutput = false) =>
await mac.run(
"/bin/bash",
["-c", `set -euo pipefail\n${helpers}\n${command}`, "bash", fakeApp],
{
cwd: process.cwd(),
encoding: "utf8",
env: {
...process.env,
CODESIGN_FAIL_AFTER_OUTPUT: failAfterOutput ? "1" : "0",
PATH: `${binDir}${path.delimiter}${process.env.PATH ?? ""}`,
},
},
);
const values = await run(
'printf "%s\\n" "$(codesign_value "$1" Authority)" "$(codesign_value_for_arch "$1" CDHash arm64)" "$(codesign_value_for_arch "$1" CDHash x86_64)"',
);
expect(values.status, values.stderr).toBe(0);
expect(values.stdout.trim().split("\n")).toEqual([
"Developer ID Application: OpenClaw Foundation (FWJYW4S8P8)",
"ARM64HASH",
"X8664HASH",
]);
const failed = await run('codesign_value_for_arch "$1" CDHash arm64', true);
expect(failed.status).toBe(7);
expect(failed.stdout).toBe("");
}));
it.concurrent("documents package and transactional lifecycle commands without probing macOS", async ({
mac,
}) =>
mac.lifetime.run(async () => {
const result = await mac.run("/bin/bash", [scriptPath, "--help"], {
cwd: process.cwd(),
encoding: "utf8",
env: { HOME: mac.createTempDir("openclaw-elevation-help-"), PATH: "/usr/bin:/bin" },
});
expect(result.status).toBe(0);
expect(result.stderr).toBe("");
expect(result.stdout).toContain("package --peekaboo-source-commit <sha>");
expect(result.stdout).toContain("verify --archive <zip> --receipt <json>");
expect(result.stdout).toContain("install --archive <zip> --receipt <json>");
expect(result.stdout).toContain(
"migration-plan [--migrate-launch-agent <plist>|--adopt-running-app]",
);
expect(result.stdout).toContain("status");
expect(result.stdout).toContain("recover");
expect(result.stdout).toContain("uninstall");
expect(result.stdout).toContain("never rewrites ordinary OpenClaw");
}));
it.concurrent("keeps the elevation service separate and fail-closed", async ({ mac }) =>
mac.lifetime.run(async () => {
const script = readFileSync(scriptPath, "utf8");
expect(script).toContain('ELEVATION_LABEL="ai.openclaw.mac.elevation-host"');
expect(script).toContain('NORMAL_LABEL="ai.openclaw.mac"');
expect(script).toContain("ordinary Launch at login is installed");
expect(script).toContain("conflicting OpenClaw launch agent is installed");
expect(script).toContain("unsupervised or conflicting OpenClaw process is running");
expect(script).toContain("plutil -insert KeepAlive -bool true");
expect(script).toContain("plutil -insert RunAtLoad -bool true");
expect(script).toContain('[$executable,"--elevation-host"]');
expect(script).toContain("automatic elevation-host rollback was incomplete");
expect(script).not.toContain("osascript");
}));
it.concurrent("runs the portable lifecycle installer without a source checkout", async ({
mac,
}) =>
mac.lifetime.run(async () => {
const harness = createArtifactVerificationHarness(mac);
const binDir = path.join(harness.env.HOME, "bin");
const launchctl = path.join(binDir, "launchctl");
writeFileSync(launchctl, "#!/bin/sh\nexit 0\n", "utf8");
chmodSync(launchctl, 0o755);
const result = await runInstaller(
mac,
harness.installerPath,
["uninstall"],
harness.env,
harness.env.HOME,
);
expect(result.status, result.stderr).toBe(0);
expect(result.stdout).toContain("Elevation launch agent removed");
}));
it.skipIf(process.platform !== "darwin").concurrent(
"plans an explicit app-backed node migration without mutating its plist",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createMigrationPlanHarness(mac);
const before = readFileSync(harness.plistPath, "utf8");
const result = await runInstaller(
mac,
scriptPath,
["migration-plan", "--app", harness.appPath, "--migrate-launch-agent", harness.plistPath],
harness.env,
);
expect(result.status, result.stderr).toBe(0);
expect(JSON.parse(result.stdout)).toEqual({
kind: "app-launch-agent",
label: harness.label,
sourcePlist: harness.plistPath,
stateDir: harness.stateDir,
configPath: harness.configPath,
expectedNodeId: "fixture-node",
loaded: false,
action: "replace-with-elevation-host",
});
expect(readFileSync(harness.plistPath, "utf8")).toBe(before);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"never deletes a pending install transaction owned by another invocation",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac);
const pendingPath = path.join(harness.stateDir, "elevation-host-install.pending.json");
writeFileSync(pendingPath, "other-install-transaction\n", "utf8");
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).toContain("incomplete elevation install transaction exists");
expect(readFileSync(pendingPath, "utf8")).toBe("other-install-transaction\n");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"rejects an explicit config path that differs from the source owner's default",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createMigrationPlanHarness(mac);
const plist = readFileSync(harness.plistPath, "utf8").replace(
`<key>OPENCLAW_CONFIG_PATH</key><string>${harness.configPath}</string>\n`,
"",
);
writeFileSync(harness.plistPath, plist, "utf8");
const result = await runInstaller(
mac,
scriptPath,
[
"migration-plan",
"--app",
harness.appPath,
"--config-path",
path.join(harness.stateDir, "other.json"),
"--migrate-launch-agent",
harness.plistPath,
],
harness.env,
);
expect(result.status).toBe(1);
expect(result.stderr).toContain(
"--config-path does not match the migration LaunchAgent OPENCLAW_CONFIG_PATH",
);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"verifies the portable installer, archive, notarized app identity, and receipt as one set",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createArtifactVerificationHarness(mac);
const unauthenticated = await runInstaller(
mac,
harness.installerPath,
["verify", "--archive", harness.archivePath, "--receipt", harness.receiptPath],
harness.env,
);
expect(unauthenticated.status).toBe(1);
expect(unauthenticated.stderr).toContain(
"verify requires --receipt-sha256 <sha256> from the authenticated release handoff",
);
const verified = await runInstaller(
mac,
harness.installerPath,
[
"verify",
"--archive",
harness.archivePath,
"--receipt",
harness.receiptPath,
...receiptDigestArgs(harness.receiptPath),
],
harness.env,
);
expect(verified.status, verified.stderr).toBe(0);
expect(verified.stdout).toContain("Elevation artifact verified");
expect(existsSync(harness.dittoMarker)).toBe(true);
const substitutedArchive = createArtifactVerificationHarness(mac);
writeFileSync(substitutedArchive.archivePath, "substituted archive", "utf8");
const rejectedBeforeExtraction = await runInstaller(
mac,
substitutedArchive.installerPath,
[
"verify",
"--archive",
substitutedArchive.archivePath,
"--receipt",
substitutedArchive.receiptPath,
...receiptDigestArgs(substitutedArchive.receiptPath),
],
substitutedArchive.env,
);
expect(rejectedBeforeExtraction.status).toBe(1);
expect(rejectedBeforeExtraction.stderr).toContain(
"artifact receipt archive digest mismatch",
);
expect(existsSync(substitutedArchive.dittoMarker)).toBe(false);
const substitutedDir = path.join(harness.env.HOME, "substituted");
mkdirSync(substitutedDir);
const substitutedInstaller = path.join(
substitutedDir,
path.basename(harness.installerPath),
);
writeExecutable(
substitutedInstaller,
`${readFileSync(harness.installerPath, "utf8")}\n# substituted\n`,
);
const substituted = await runInstaller(
mac,
substitutedInstaller,
[
"verify",
"--archive",
harness.archivePath,
"--receipt",
harness.receiptPath,
...receiptDigestArgs(harness.receiptPath),
],
harness.env,
);
expect(substituted.status).toBe(1);
expect(substituted.stderr).toContain("artifact receipt installer digest mismatch");
writeFileSync(
harness.receiptPath,
JSON.stringify({ ...harness.receipt, archiveSha256: "0".repeat(64) }),
"utf8",
);
const rejected = await runInstaller(
mac,
harness.installerPath,
[
"verify",
"--archive",
harness.archivePath,
"--receipt",
harness.receiptPath,
"--receipt-sha256",
sha256(JSON.stringify(harness.receipt)),
],
harness.env,
);
expect(rejected.status).toBe(1);
expect(rejected.stderr).toContain(
"artifact receipt does not match the authenticated release handoff digest",
);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"rejects CUA-bearing elevation artifacts before verify or install",
async ({ mac }) =>
mac.lifetime.run(async () => {
const verification = createArtifactVerificationHarness(mac);
const rejectedVerify = await runInstaller(
mac,
verification.installerPath,
[
"verify",
"--archive",
verification.archivePath,
"--receipt",
verification.receiptPath,
...receiptDigestArgs(verification.receiptPath),
],
{ ...verification.env, TEST_CUA_DRIVER_KIND: "file" },
);
expect(rejectedVerify.status).toBe(1);
expect(rejectedVerify.stderr).toContain("must not contain bundled CUA driver");
const installation = createInstallRollbackHarness(mac);
const rejectedInstall = await runAuthenticatedMigrationInstall(mac, installation, {
...installation.env,
TEST_CUA_DRIVER_KIND: "symlink",
});
expect(rejectedInstall.status).toBe(1);
expect(rejectedInstall.stderr).toContain("must not contain bundled CUA driver");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"plans canonical node conversion without reading or copying its token",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createCanonicalNodeMigrationHarness(mac);
const result = await runInstaller(
mac,
scriptPath,
["migration-plan", "--app", harness.appPath, "--migrate-launch-agent", harness.plistPath],
harness.env,
);
expect(result.status, result.stderr).toBe(0);
expect(JSON.parse(result.stdout)).toMatchObject({
kind: "canonical-node",
label: harness.label,
stateDir: harness.stateDir,
configPath: harness.configPath,
expectedNodeId: "fixture-node",
loaded: true,
});
expect(result.stdout).not.toContain("ignored-secret-shape");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"requires a reusable PATH CLI before converting a canonical node owner",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createCanonicalNodeMigrationHarness(mac);
const binDir = path.join(harness.env.HOME, "bin");
rmSync(path.join(binDir, "openclaw"));
const jqPath = (
await mac.run("bash", ["-lc", "command -v jq"], {
encoding: "utf8",
})
).stdout.trim();
symlinkSync(jqPath, path.join(binDir, "jq"));
const result = await runInstaller(
mac,
scriptPath,
["migration-plan", "--app", harness.appPath, "--migrate-launch-agent", harness.plistPath],
{
...harness.env,
PATH: `${binDir}:/usr/bin:/bin:/usr/sbin:/sbin`,
},
);
expect(result.status).toBe(1);
expect(result.stderr).toContain("openclaw CLI is required for gateway node attestation");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"revalidates canonical node environment and wrapper sidecars",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createCanonicalNodeMigrationHarness(mac);
const wrapperPath = `${harness.envPath.slice(0, -4)}-env-wrapper.sh`;
const envContents = readFileSync(harness.envPath, "utf8");
const wrapperContents = readFileSync(wrapperPath, "utf8");
const envSha = sha256(envContents);
const wrapperSha = sha256(wrapperContents);
const clean = await runCanonicalNodeSidecarVerifier(mac, harness, envSha, wrapperSha);
expect(clean.status, clean.stderr).toBe(0);
writeFileSync(harness.envPath, `${envContents}\n`, "utf8");
const changedEnv = await runCanonicalNodeSidecarVerifier(mac, harness, envSha, wrapperSha);
expect(changedEnv.status).toBe(1);
writeFileSync(harness.envPath, envContents, "utf8");
writeFileSync(wrapperPath, `${wrapperContents}\n`, "utf8");
const changedWrapper = await runCanonicalNodeSidecarVerifier(
mac,
harness,
envSha,
wrapperSha,
);
expect(changedWrapper.status).toBe(1);
const customWrapper = "#!/bin/sh\nexec /usr/bin/false\n";
writeFileSync(wrapperPath, customWrapper, "utf8");
const recapturedCustomWrapper = await runCanonicalNodeSidecarVerifier(
mac,
harness,
envSha,
sha256(customWrapper),
);
expect(recapturedCustomWrapper.status).toBe(1);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"binds canonical node receipt metadata to its authenticated migration plist",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createCanonicalNodeMigrationHarness(mac);
const clean = await runMigrationReceiptBindingVerifier(mac, harness, "canonical-node");
expect(clean.status, clean.stderr).toBe(0);
const downgraded = await runMigrationReceiptBindingVerifier(
mac,
harness,
"app-launch-agent",
);
expect(downgraded.status).toBe(1);
const alternateEnv = path.join(harness.stateDir, "service-env", "alternate.env");
const alternateWrapper = `${alternateEnv.slice(0, -4)}-env-wrapper.sh`;
writeFileSync(alternateEnv, readFileSync(harness.envPath));
chmodSync(alternateEnv, 0o600);
writeFileSync(
alternateWrapper,
readFileSync(`${harness.envPath.slice(0, -4)}-env-wrapper.sh`),
);
chmodSync(alternateWrapper, 0o700);
const substituted = await runMigrationReceiptBindingVerifier(
mac,
harness,
"canonical-node",
{
envPath: alternateEnv,
wrapperPath: alternateWrapper,
},
);
expect(substituted.status).toBe(1);
const originalEnv = readFileSync(harness.envPath);
const originalIdentity = fileIdentity(harness.envPath);
const replacementEnv = `${harness.envPath}.replacement`;
writeFileSync(replacementEnv, originalEnv);
chmodSync(replacementEnv, 0o600);
renameSync(replacementEnv, harness.envPath);
expect(fileIdentity(harness.envPath)).not.toBe(originalIdentity);
const replacedIdentity = await runMigrationReceiptBindingVerifier(
mac,
harness,
"canonical-node",
{
envIdentity: originalIdentity,
},
);
expect(replacedIdentity.status).toBe(1);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"rejects a relative CLI resolution before converting a canonical node owner",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createCanonicalNodeMigrationHarness(mac);
const relativeBin = path.join(harness.env.HOME, "relative-cli");
mkdirSync(relativeBin);
writeExecutable(path.join(relativeBin, "openclaw"), "#!/bin/sh\nexit 0\n");
const relativePathEntry = path.relative(process.cwd(), relativeBin);
const result = await runInstaller(
mac,
scriptPath,
["migration-plan", "--app", harness.appPath, "--migrate-launch-agent", harness.plistPath],
{
...harness.env,
PATH: `${relativePathEntry}:${harness.env.PATH}`,
},
);
expect(result.status).toBe(1);
expect(result.stderr).toContain("openclaw CLI is required for gateway node attestation");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"rejects a canonical node override that differs from the selected paired app identity",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createCanonicalNodeMigrationHarness(mac, "different-node");
const result = await runInstaller(
mac,
scriptPath,
["migration-plan", "--app", harness.appPath, "--migrate-launch-agent", harness.plistPath],
harness.env,
);
expect(result.status).toBe(1);
expect(result.stderr).toContain(
"canonical node LaunchAgent --node-id does not match the selected paired macOS identity",
);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"rejects a relative canonical node state directory",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createCanonicalNodeMigrationHarness(mac);
writeFileSync(
harness.envPath,
[
"# Generated by OpenClaw. Do not edit while the gateway service is installed.",
"export OPENCLAW_STATE_DIR='relative-state'",
`export OPENCLAW_CONFIG_PATH='${harness.configPath}'`,
"",
].join("\n"),
"utf8",
);
const result = await runInstaller(
mac,
scriptPath,
["migration-plan", "--app", harness.appPath, "--migrate-launch-agent", harness.plistPath],
harness.env,
);
expect(result.status).toBe(1);
expect(result.stderr).toContain("canonical node OPENCLAW_STATE_DIR must be absolute");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"rejects a later noncanonical statement that overrides canonical node paths",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createCanonicalNodeMigrationHarness(mac);
writeFileSync(
harness.envPath,
`${readFileSync(harness.envPath, "utf8")}OPENCLAW_STATE_DIR=/attacker-selected\n`,
"utf8",
);
const result = await runInstaller(
mac,
scriptPath,
["migration-plan", "--app", harness.appPath, "--migrate-launch-agent", harness.plistPath],
harness.env,
);
expect(result.status).toBe(1);
expect(result.stderr).toContain(
"canonical node environment contains a noncanonical statement",
);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"rejects canonical node environments with non-LF line endings",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createCanonicalNodeMigrationHarness(mac);
writeFileSync(
harness.envPath,
readFileSync(harness.envPath, "utf8").replaceAll("\n", "\r\n"),
"utf8",
);
const result = await runInstaller(
mac,
scriptPath,
["migration-plan", "--app", harness.appPath, "--migrate-launch-agent", harness.plistPath],
harness.env,
);
expect(result.status).toBe(1);
expect(result.stderr).toContain("canonical node environment must use LF line endings");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"plans explicit adoption of one unsupervised background app",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createMigrationPlanHarness(mac);
addRunningAppFixture(harness);
const result = await runInstaller(
mac,
scriptPath,
[
"migration-plan",
"--app",
harness.appPath,
"--state-dir",
harness.stateDir,
"--config-path",
harness.configPath,
"--adopt-running-app",
],
harness.env,
);
expect(result.status, result.stderr).toBe(0);
expect(JSON.parse(result.stdout)).toMatchObject({
kind: "running-app",
label: null,
sourcePlist: null,
stateDir: harness.stateDir,
configPath: harness.configPath,
expectedNodeId: "fixture-node",
loaded: false,
});
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"refuses to adopt the launchd-owned elevation process",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createMigrationPlanHarness(mac, "loaded");
addRunningAppFixture(harness);
const result = await runInstaller(
mac,
scriptPath,
[
"migration-plan",
"--app",
harness.appPath,
"--state-dir",
harness.stateDir,
"--config-path",
harness.configPath,
"--adopt-running-app",
],
harness.env,
);
expect(result.status).toBe(1);
expect(result.stderr).toContain("adoption refuses the launchd-owned elevation process");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"fails closed when launchd ownership cannot be inspected for migration",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createMigrationPlanHarness(mac, "error");
const result = await runInstaller(
mac,
scriptPath,
["migration-plan", "--app", harness.appPath, "--migrate-launch-agent", harness.plistPath],
harness.env,
);
expect(result.status).toBe(1);
expect(result.stderr).toContain("launchd ownership state could not be inspected");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"restores the exact app, source LaunchAgent, and loaded state when cutover launchd bootstrap fails",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac);
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const priorFailedPath = `${harness.appPath}.failed-elevation-host-${"a".repeat(40)}`;
mkdirSync(priorFailedPath);
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).toContain("could not bootstrap elevation host");
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
expect(existsSync(priorFailedPath)).toBe(true);
expect(
existsSync(
path.join(
harness.env.HOME,
"Library",
"LaunchAgents",
"ai.openclaw.mac.elevation-host.plist",
),
),
).toBe(false);
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.json"))).toBe(false);
}),
);
for (const rollbackCuaDriverKind of ["file", "symlink"] as const) {
it.skipIf(process.platform !== "darwin").concurrent(
`preserves but never restarts a ${rollbackCuaDriverKind} CUA-bearing elevation rollback`,
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
existingElevationLoaded: true,
reloadElevationAfterQuarantine: true,
rollbackCuaDriverKind,
});
const result = await runInstaller(
mac,
harness.installerPath,
[
"install",
"--archive",
harness.archivePath,
"--receipt",
harness.receiptPath,
...receiptDigestArgs(harness.receiptPath),
"--app",
harness.appPath,
"--state-dir",
harness.stateDir,
"--config-path",
harness.configPath,
],
harness.env,
);
expect(result.status).toBe(1);
expect(result.stderr).toContain("could not bootstrap elevation host");
expect(result.stderr).toContain(
"Preserved previous elevation app with bundled CUA driver",
);
expect(result.stderr).toContain(
"Quarantined replacement for unsafe previous elevation LaunchAgent",
);
expect(result.stderr).toContain("automatic elevation-host rollback was incomplete");
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-absent");
const quarantinedApp = preservedCuaAppPath(harness);
expect(quarantinedApp).toBeDefined();
const rollbackDriver = lstatSync(
path.join(quarantinedApp!, "Contents", "Resources", "cua-driver"),
);
expect(rollbackDriver.isSymbolicLink()).toBe(rollbackCuaDriverKind === "symlink");
expect(existsSync(harness.elevationPlist)).toBe(false);
expect(
readdirSync(path.dirname(harness.elevationPlist)).filter((name) =>
name.startsWith("ai.openclaw.mac.elevation-host"),
),
).toEqual([]);
const previousPlist = readdirSync(harness.stateDir).find((name) =>
name.startsWith("elevation-host.previous-plist."),
);
expect(previousPlist).toBeDefined();
expect(readFileSync(path.join(harness.stateDir, previousPlist!), "utf8")).toBe(
harness.elevationPlistContents,
);
const quarantinedPlist = readdirSync(harness.stateDir).find((name) =>
name.startsWith("elevation-host.quarantined-launch-agent."),
);
expect(quarantinedPlist).toBeDefined();
expect(readFileSync(path.join(harness.stateDir, quarantinedPlist!), "utf8")).toContain(
"--elevation-host",
);
}),
);
}
it.skipIf(process.platform !== "darwin").concurrent(
"removes the discoverable elevation plist when unsafe rollback quarantine fails",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
existingElevationLoaded: true,
failElevationQuarantineRename: true,
rollbackCuaDriverKind: "file",
});
const result = await runInstaller(
mac,
harness.installerPath,
[
"install",
"--archive",
harness.archivePath,
"--receipt",
harness.receiptPath,
...receiptDigestArgs(harness.receiptPath),
"--app",
harness.appPath,
"--state-dir",
harness.stateDir,
"--config-path",
harness.configPath,
],
harness.env,
);
expect(result.status).toBe(1);
expect(result.stderr).toContain(
"Removed unquarantinable replacement for unsafe previous elevation LaunchAgent",
);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-absent");
expect(existsSync(harness.elevationPlist)).toBe(false);
const previousPlist = readdirSync(harness.stateDir).find((name) =>
name.startsWith("elevation-host.previous-plist."),
);
expect(previousPlist).toBeDefined();
expect(readFileSync(path.join(harness.stateDir, previousPlist!), "utf8")).toBe(
harness.elevationPlistContents,
);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"quarantines a CUA-bearing elevation app when the recorded rollback destination is blocked",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
danglingRollbackDuringMove: true,
existingElevationLoaded: true,
rollbackCuaDriverKind: "file",
});
const result = await runInstaller(
mac,
harness.installerPath,
[
"install",
"--archive",
harness.archivePath,
"--receipt",
harness.receiptPath,
...receiptDigestArgs(harness.receiptPath),
"--app",
harness.appPath,
"--state-dir",
harness.stateDir,
"--config-path",
harness.configPath,
],
harness.env,
);
expect(result.status).toBe(1);
expect(existsSync(path.join(harness.appPath, "Contents", "Resources", "cua-driver"))).toBe(
false,
);
expect(existsSync(harness.elevationPlist)).toBe(false);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-absent");
const quarantinedApp = quarantinedElevationAppPath(harness.stateDir);
expect(quarantinedApp).toBeDefined();
expect(existsSync(path.join(quarantinedApp!, "Contents", "Resources", "cua-driver"))).toBe(
true,
);
}),
);
for (const evidence of ["job", "plist", "plist-program", "receipt"] as const) {
it.skipIf(process.platform !== "darwin").concurrent(
`quarantines a symlinked CUA app from exact ${evidence} ownership evidence`,
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
rollbackCuaDriverKind: "file",
unsafeEntryEvidence: evidence,
});
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).toContain("Quarantined CUA-bearing elevation app");
expect(lstatSync(harness.appPath, { throwIfNoEntry: false })).toBeUndefined();
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-absent");
const quarantinedApp = quarantinedElevationAppPath(harness.stateDir);
expect(quarantinedApp).toBeDefined();
expect(lstatSync(quarantinedApp!).isSymbolicLink()).toBe(true);
expect(
existsSync(path.join(quarantinedApp!, "Contents", "Resources", "cua-driver")),
).toBe(true);
}),
);
}
it.skipIf(process.platform !== "darwin").concurrent(
"keeps a symlink quarantined when its target changes after the move",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
removeCuaDriverAfterUnsafeEntryMove: true,
rollbackCuaDriverKind: "file",
unsafeEntryEvidence: "job",
});
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).toContain("Quarantined CUA-bearing elevation app");
const quarantinedApp = quarantinedElevationAppPath(harness.stateDir);
expect(quarantinedApp).toBeDefined();
expect(lstatSync(quarantinedApp!).isSymbolicLink()).toBe(true);
}),
);
for (const evidence of ["unrelated-plist", "unrelated-program", "unrelated-receipt"] as const) {
it.skipIf(process.platform !== "darwin").concurrent(
`preserves an app when only an ${evidence} remains`,
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
rollbackCuaDriverKind: "file",
unsafeEntryEvidence: evidence,
});
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).not.toContain("Quarantined CUA-bearing elevation app");
expect(lstatSync(harness.appPath).isDirectory()).toBe(true);
expect(quarantinedElevationAppPath(harness.stateDir)).toBeUndefined();
const stalePath =
evidence !== "unrelated-receipt"
? harness.elevationPlist
: path.join(harness.stateDir, "elevation-host-install.json");
expect(existsSync(stalePath)).toBe(true);
}),
);
}
for (const setupFailure of ["identity", "mktemp"] as const) {
it.skipIf(process.platform !== "darwin").concurrent(
`neutralizes launchd before unsafe app quarantine ${setupFailure} setup fails`,
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
danglingRollbackDuringMove: true,
existingElevationLoaded: true,
failUnsafeEntryIdentity: setupFailure === "identity",
failUnsafeEntryMktemp: setupFailure === "mktemp",
rollbackCuaDriverKind: "file",
});
const result = await runInstaller(
mac,
harness.installerPath,
[
"install",
"--archive",
harness.archivePath,
"--receipt",
harness.receiptPath,
...receiptDigestArgs(harness.receiptPath),
"--app",
harness.appPath,
"--state-dir",
harness.stateDir,
"--config-path",
harness.configPath,
],
harness.env,
);
expect(result.status).toBe(1);
expect(result.stderr).toContain("automatic elevation-host rollback was incomplete");
expect(existsSync(harness.elevationPlist)).toBe(false);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-absent");
expect(
existsSync(path.join(harness.appPath, "Contents", "Resources", "cua-driver")),
).toBe(true);
}),
);
}
it.skipIf(process.platform !== "darwin").concurrent(
"neutralizes unsafe rollback even when its plist evidence becomes corrupt",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
corruptRollbackPlistBackupOnSync: true,
existingElevationLoaded: true,
rollbackCuaDriverKind: "file",
});
const result = await runInstaller(
mac,
harness.installerPath,
[
"install",
"--archive",
harness.archivePath,
"--receipt",
harness.receiptPath,
...receiptDigestArgs(harness.receiptPath),
"--app",
harness.appPath,
"--state-dir",
harness.stateDir,
"--config-path",
harness.configPath,
],
harness.env,
);
expect(result.status).toBe(1);
expect(result.stderr).toContain("automatic elevation-host rollback was incomplete");
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-absent");
expect(existsSync(harness.appPath)).toBe(false);
expect(existsSync(harness.elevationPlist)).toBe(false);
expect(
readdirSync(harness.stateDir).some((name) =>
name.startsWith("elevation-host.quarantined-launch-agent."),
),
).toBe(true);
expect(
existsSync(
path.join(preservedCuaAppPath(harness)!, "Contents", "Resources", "cua-driver"),
),
).toBe(true);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"never restores a CUA-bearing current elevation job after explicit recovery fails",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
currentCuaSignatureInvalid: true,
launchdBootstrapFails: false,
migrationRestoreBootstrapFails: true,
});
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const currentReceipt = readFileSync(
path.join(harness.stateDir, "elevation-host-install.json"),
"utf8",
);
const resources = path.join(harness.appPath, "Contents", "Resources");
mkdirSync(resources, { recursive: true });
writeExecutable(path.join(resources, "cua-driver"), "#!/bin/sh\nexit 0\n");
const recovered = await runAuthenticatedElevationRecovery(mac, harness);
expect(recovered.status).toBe(1);
expect(recovered.stderr).toContain(
"Preserved current elevation app with bundled CUA driver",
);
expect(recovered.stderr).toContain(
"recovery failed and the current OpenClaw installation could not be restored completely",
);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-absent");
expect(existsSync(harness.elevationPlist)).toBe(false);
expect(existsSync(path.join(harness.appPath, "Contents", "Resources", "cua-driver"))).toBe(
false,
);
const preservedApp = preservedCuaAppPath(harness);
expect(preservedApp).toBeDefined();
expect(existsSync(path.join(preservedApp!, "Contents", "Resources", "cua-driver"))).toBe(
true,
);
expect(
readFileSync(path.join(harness.stateDir, "elevation-host-install.json"), "utf8"),
).toBe(currentReceipt);
const preservedCurrentPlist = readdirSync(harness.stateDir).find((name) =>
name.startsWith("elevation-host.recovery-current-plist."),
);
expect(preservedCurrentPlist).toBeDefined();
expect(readFileSync(path.join(harness.stateDir, preservedCurrentPlist!), "utf8")).toContain(
"--elevation-host",
);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"refuses to record an invalid existing app as rollback state",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac);
writeFileSync(
path.join(harness.appPath, "Contents", "invalid-signature"),
"invalid\n",
"utf8",
);
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).toContain(
"installed OpenClaw app does not pass strict signature and identity validation",
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.json"))).toBe(false);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"never removes a replacement LaunchAgent created while the source owner exits",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
launchdBootstrapFails: false,
recreateSourceDuringBootout: true,
});
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).toContain(
"migration LaunchAgent path was recreated before cutover commit",
);
expect(result.stderr).toContain("automatic elevation-host rollback was incomplete");
expect(readFileSync(harness.sourcePlist, "utf8")).toBe("replacement-owner\n");
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-absent");
const backupName = readdirSync(harness.stateDir).find((name) =>
name.startsWith("elevation-host.previous-launch-agent."),
);
expect(backupName).toBeDefined();
expect(readFileSync(path.join(harness.stateDir, backupName!), "utf8")).toBe(
harness.sourceContents,
);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"restores exact source ownership when termination arrives during custody transfer",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { signalDuringCustody: true });
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.signal).toBe("SIGTERM");
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
expect(readdirSync(path.dirname(harness.sourcePlist))).not.toContainEqual(
expect.stringContaining(".custody."),
);
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.json"))).toBe(false);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"never overwrites a raced migration custody destination",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
raceMigrationCustodyDestination: true,
});
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).toContain("could not take exact custody");
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
const custodyName = readdirSync(path.dirname(harness.sourcePlist)).find((name) =>
name.startsWith(`${path.basename(harness.sourcePlist)}.custody.`),
);
expect(custodyName).toBeDefined();
expect(
readFileSync(path.join(path.dirname(harness.sourcePlist), custodyName!), "utf8"),
).toBe("raced-custody-owner\n");
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.json"))).toBe(false);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"restores the authenticated source while preserving a same-content custody replacement",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
replaceMigrationSourceSameContentBeforeInitialCustody: true,
});
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).toContain("could not take exact custody");
expect(result.stderr).toContain("automatic elevation-host rollback was incomplete");
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
const custodyName = readdirSync(path.dirname(harness.sourcePlist)).find((name) =>
name.startsWith(`${path.basename(harness.sourcePlist)}.custody.`),
);
expect(custodyName).toBeDefined();
expect(
readFileSync(path.join(path.dirname(harness.sourcePlist), custodyName!), "utf8"),
).toBe(harness.sourceContents);
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.pending.json"))).toBe(
true,
);
}),
);
it
.skipIf(process.platform !== "darwin")
.concurrent.for(
(["killAfterInitialMigrationCustody", "killAfterPendingReceipt"] as const).flatMap(
(killPoint) =>
(["valid", "arm64", "x86_64"] as const).map((identity) => ({ killPoint, identity })),
),
)(
"binds $killPoint recovery to the recorded prior app ($identity)",
async ({ killPoint, identity }, { mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { [killPoint]: true });
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const pendingPath = path.join(harness.stateDir, "elevation-host-install.pending.json");
const interrupted = await runAuthenticatedMigrationInstall(mac, harness);
expect(interrupted.signal).toBe("SIGKILL");
expect(existsSync(harness.sourcePlist)).toBe(killPoint === "killAfterPendingReceipt");
expect(existsSync(pendingPath)).toBe(true);
expect(existsSync(path.join(harness.appPath, "Contents", "Resources", "node-worker"))).toBe(
false,
);
if (identity !== "valid") {
const pending = JSON.parse(readFileSync(pendingPath, "utf8")) as {
backupCDHashes: Record<"arm64" | "x86_64", string>;
};
pending.backupCDHashes[identity] = "0".repeat(40);
writeFileSync(pendingPath, JSON.stringify(pending));
}
const recovered = await runAuthenticatedElevationRecovery(mac, harness);
expect(recovered.status, recovered.stderr).toBe(identity === "valid" ? 0 : 1);
if (identity !== "valid") {
expect(recovered.stderr).toContain(
"receipt app backup is missing, symlinked, or not a bundle directory",
);
}
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
const sourceRestored = identity === "valid" || killPoint === "killAfterPendingReceipt";
expect(existsSync(harness.sourcePlist)).toBe(sourceRestored);
if (sourceRestored) {
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
}
expect(existsSync(pendingPath)).toBe(identity !== "valid");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"rejects a same-content migration source replacement after install process death",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
killAfterInitialMigrationCustody: true,
});
const interrupted = await runAuthenticatedMigrationInstall(mac, harness);
expect(interrupted.signal).toBe("SIGKILL");
writeFileSync(harness.sourcePlist, harness.sourceContents, "utf8");
const recovered = await runAuthenticatedElevationRecovery(mac, harness);
expect(recovered.status).toBe(1);
expect(recovered.stderr).toContain(
"pending migration source identity no longer matches the prepared transaction",
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.pending.json"))).toBe(
true,
);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"recovers a persisted install killed after rollback app custody",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { killAfterRollbackAppCustody: true });
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const interrupted = await runAuthenticatedMigrationInstall(mac, harness);
expect(interrupted.signal).toBe("SIGKILL");
expect(existsSync(harness.appPath)).toBe(false);
const recovered = await runAuthenticatedElevationRecovery(mac, harness);
expect(recovered.status, recovered.stderr).toBe(0);
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.pending.json"))).toBe(
false,
);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"rejects a replaced authenticated rename helper without fallback",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
replaceAuthenticatedRenameHelperBeforeUse: true,
});
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).toContain("authenticated elevation helper could not sync");
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.json"))).toBe(false);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"restores exact source ownership when hangup arrives during custody transfer",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { hupDuringCustody: true });
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.signal).toBe("SIGHUP");
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
expect(readdirSync(path.dirname(harness.sourcePlist))).not.toContainEqual(
expect.stringContaining(".custody."),
);
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.json"))).toBe(false);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"keeps a same-source prior app canonical when its rollback move never starts",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
sameSourceExistingApp: true,
signalBeforeRollbackAppMove: true,
});
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.signal).toBe("SIGTERM");
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.json"))).toBe(false);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"restores source ownership when a dangling rollback destination races the exclusive move",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { danglingRollbackDuringMove: true });
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).toContain("could not take verified custody");
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"refuses cutover when an app-backed owner restarts before bootout completes",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { restartAppDuringBootout: true });
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).toContain("an OpenClaw app process survived owner shutdown");
expect(result.stderr).toContain("automatic elevation-host rollback was incomplete");
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(existsSync(harness.sourcePlist)).toBe(false);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-absent");
const backupName = readdirSync(harness.stateDir).find((name) =>
name.startsWith("elevation-host.previous-launch-agent."),
);
expect(backupName).toBeDefined();
expect(readFileSync(path.join(harness.stateDir, backupName!), "utf8")).toBe(
harness.sourceContents,
);
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.json"))).toBe(false);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"rechecks launchd after a transient replacement app process exits",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { transientAppRestartReloadsJob: true });
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).toContain("migration LaunchAgent reloaded during owner shutdown");
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.json"))).toBe(false);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"never treats a live but uninspectable OpenClaw PID as quiescent",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { failLsofInspection: true });
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).toContain("an OpenClaw app process survived owner shutdown");
expect(result.stderr).toContain("automatic elevation-host rollback was incomplete");
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(existsSync(harness.sourcePlist)).toBe(false);
const backupName = readdirSync(harness.stateDir).find((name) =>
name.startsWith("elevation-host.previous-launch-agent."),
);
expect(backupName).toBeDefined();
expect(readFileSync(path.join(harness.stateDir, backupName!), "utf8")).toBe(
harness.sourceContents,
);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"never treats a pgrep inspection error as quiescence",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { failPgrepInspection: true });
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).toContain("an OpenClaw app process survived owner shutdown");
expect(result.stderr).toContain("automatic elevation-host rollback was incomplete");
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(existsSync(harness.sourcePlist)).toBe(false);
const backupName = readdirSync(harness.stateDir).find((name) =>
name.startsWith("elevation-host.previous-launch-agent."),
);
expect(backupName).toBeDefined();
expect(readFileSync(path.join(harness.stateDir, backupName!), "utf8")).toBe(
harness.sourceContents,
);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"rejects a rollback app whose non-native architecture fails signature validation",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
rollbackNonNativeSignatureInvalid: true,
});
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).toContain(
"installed OpenClaw app does not pass strict signature and identity validation",
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.json"))).toBe(false);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"preserves rollback evidence when automatic recovery cannot restore the source owner",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { recreateSourceOnFailure: true });
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).toContain("automatic elevation-host rollback was incomplete");
expect(readFileSync(harness.sourcePlist, "utf8")).toBe("replacement-owner\n");
const backupName = readdirSync(harness.stateDir).find((name) =>
name.startsWith("elevation-host.previous-launch-agent."),
);
expect(backupName).toBeDefined();
expect(readFileSync(path.join(harness.stateDir, backupName!), "utf8")).toBe(
harness.sourceContents,
);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"commits only after the exact macOS computer-use node reconnects through the new app",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { launchdBootstrapFails: false });
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status, result.stderr).toBe(0);
expect(result.stdout).toContain("Elevation host installed: pid=555555");
expect(existsSync(harness.sourcePlist)).toBe(false);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-loaded");
const installReceipt = JSON.parse(
readFileSync(path.join(harness.stateDir, "elevation-host-install.json"), "utf8"),
) as Record<string, unknown>;
expect(installReceipt).toMatchObject({
kind: "openclaw-elevation-install",
nodeId: "fixture-node",
nodeProfile: "primary",
});
expect(installReceipt.migration).toMatchObject({
label: harness.label,
wasLoaded: true,
});
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"reserves generation-unique rollback custody across repeated same-artifact installs",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
launchdBootstrapFails: false,
sameSourceExistingApp: true,
});
const first = await runAuthenticatedMigrationInstall(mac, harness);
expect(first.status, first.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const firstReceipt = JSON.parse(readFileSync(installReceiptPath, "utf8")) as {
backupPath: string;
};
const second = await runInstaller(
mac,
harness.installerPath,
[
"install",
"--archive",
harness.archivePath,
"--receipt",
harness.receiptPath,
...receiptDigestArgs(harness.receiptPath),
"--app",
harness.appPath,
"--state-dir",
harness.stateDir,
],
harness.env,
);
expect(second.status, second.stderr).toBe(0);
const secondReceipt = JSON.parse(readFileSync(installReceiptPath, "utf8")) as {
backupPath: string;
previousReceipt: string;
};
expect(firstReceipt.backupPath).toMatch(/[.][A-Za-z0-9]{6}$/);
expect(secondReceipt.backupPath).toMatch(/[.][A-Za-z0-9]{6}$/);
expect(secondReceipt.backupPath).not.toBe(firstReceipt.backupPath);
expect(existsSync(firstReceipt.backupPath)).toBe(true);
expect(existsSync(secondReceipt.backupPath)).toBe(true);
expect(existsSync(secondReceipt.previousReceipt)).toBe(true);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"rolls back when final installed code identity diverges before receipt commit",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
finalCDHashMismatch: true,
launchdBootstrapFails: false,
});
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).toContain("artifact receipt x86_64 CDHash mismatch");
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.json"))).toBe(false);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"rolls back when the final installed signature becomes invalid before receipt commit",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
finalSignatureInvalid: true,
launchdBootstrapFails: false,
});
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.json"))).toBe(false);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"uses the authenticated extracted helper when the installed executable disappears",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
launchdBootstrapFails: false,
removeInstalledExecutableAfterReadiness: true,
});
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.json"))).toBe(false);
expect(
readdirSync(harness.env.HOME).some((name) =>
name.startsWith("InstalledOpenClaw.app.failed-elevation-host-"),
),
).toBe(true);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"commits the receipt and cutover marker before replaying termination",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
launchdBootstrapFails: false,
signalDuringReceiptCommit: true,
});
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.signal).toBe("SIGTERM");
expect(existsSync(harness.sourcePlist)).toBe(false);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-loaded");
const receipt = JSON.parse(
readFileSync(path.join(harness.stateDir, "elevation-host-install.json"), "utf8"),
) as { sourceCommit: string };
expect(receipt.sourceCommit).toBe(harness.sourceCommit);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"removes an ambiguously published first-install receipt during rollback",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
failAfterReceiptCommitMove: true,
launchdBootstrapFails: false,
});
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const result = await runAuthenticatedMigrationInstall(mac, harness);
expect(result.status).toBe(1);
expect(result.stderr).toContain("could not atomically publish the install receipt");
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.json"))).toBe(false);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"rejects managed upgrades that change the recorded config or node profile",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { launchdBootstrapFails: false });
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const mismatchedConfig = await runInstaller(
mac,
harness.installerPath,
[
"install",
"--archive",
harness.archivePath,
"--receipt",
harness.receiptPath,
...receiptDigestArgs(harness.receiptPath),
"--app",
harness.appPath,
"--state-dir",
harness.stateDir,
"--config-path",
path.join(harness.stateDir, "other.json"),
],
harness.env,
);
expect(mismatchedConfig.status).toBe(1);
expect(mismatchedConfig.stderr).toContain(
"--config-path does not match the existing elevation install receipt",
);
writeExecutable(
path.join(harness.env.HOME, "bin", "defaults"),
"#!/bin/sh\nprintf '%s\\n' node\n",
);
const mismatchedProfile = await runInstaller(
mac,
harness.installerPath,
[
"install",
"--archive",
harness.archivePath,
"--receipt",
harness.receiptPath,
...receiptDigestArgs(harness.receiptPath),
"--app",
harness.appPath,
"--state-dir",
harness.stateDir,
],
harness.env,
);
expect(mismatchedProfile.status).toBe(1);
expect(mismatchedProfile.stderr).toContain(
"managed upgrade identity does not match the existing elevation install receipt",
);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"explicitly recovers the prior app and source job from the verified install receipt",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { launchdBootstrapFails: false });
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const recovered = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(recovered.status, recovered.stderr).toBe(0);
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.json"))).toBe(false);
expect(
readdirSync(harness.stateDir).some((name) =>
name.startsWith("elevation-host.recovered-receipt."),
),
).toBe(true);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"restores a verified backup when the current app is missing",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { launchdBootstrapFails: false });
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
rmSync(harness.appPath, { recursive: true });
const unauthenticated = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(unauthenticated.status).toBe(1);
expect(unauthenticated.stderr).toContain(
"recovery requires the authenticated elevation archive",
);
expect(existsSync(installReceiptPath)).toBe(true);
const recovered = await runAuthenticatedElevationRecovery(mac, harness);
expect(recovered.status, recovered.stderr).toBe(0);
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
expect(existsSync(installReceiptPath)).toBe(false);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"resumes exact app and migration custody after process death",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
killDuringMigrationRestoreBootstrapOnce: true,
launchdBootstrapFails: false,
});
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const interrupted = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(interrupted.status).toBeNull();
expect(interrupted.signal).toBe("SIGKILL");
expect(existsSync(installReceiptPath)).toBe(true);
expect(existsSync(harness.sourcePlist)).toBe(true);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-absent");
const resumed = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(resumed.status, resumed.stderr).toBe(0);
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
expect(existsSync(installReceiptPath)).toBe(false);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"resumes after the restored prior owner restarted before process death",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
killAfterMigrationRestoreBootstrapOnce: true,
launchdBootstrapFails: false,
});
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const interrupted = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(interrupted.signal).toBe("SIGKILL");
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
const receiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const recordedIdentity = (
await mac.run(
"/usr/bin/xattr",
["-p", "com.openclaw.elevation.recovery-migration-identity", receiptPath],
{ encoding: "utf8" },
)
).stdout.trim();
expect(recordedIdentity).toBe(
await durableFileIdentity(mac, harness.sourcePlist, harness.env),
);
const resumed = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(resumed.status, resumed.stderr).toBe(0);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"resumes an initially absent app after process death",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
killDuringMigrationRestoreBootstrapOnce: true,
launchdBootstrapFails: false,
});
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
rmSync(harness.appPath, { recursive: true });
const recoveryArgs = [
"recover",
"--archive",
harness.archivePath,
"--receipt",
harness.receiptPath,
...receiptDigestArgs(harness.receiptPath),
"--app",
harness.appPath,
"--state-dir",
harness.stateDir,
];
const interrupted = await runInstaller(
mac,
harness.installerPath,
recoveryArgs,
harness.env,
);
expect(interrupted.status).toBeNull();
expect(interrupted.signal).toBe("SIGKILL");
expect(existsSync(installReceiptPath)).toBe(true);
expect(existsSync(harness.appPath)).toBe(true);
const resumed = await runInstaller(mac, harness.installerPath, recoveryArgs, harness.env);
expect(resumed.status, resumed.stderr).toBe(0);
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
expect(existsSync(installReceiptPath)).toBe(false);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"fails closed when a durable recovery binding cannot be read",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
killDuringMigrationRestoreBootstrapOnce: true,
launchdBootstrapFails: false,
});
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const recoveryArgs = ["recover", "--app", harness.appPath, "--state-dir", harness.stateDir];
const interrupted = await runInstaller(
mac,
harness.installerPath,
recoveryArgs,
harness.env,
);
expect(interrupted.signal).toBe("SIGKILL");
const unreadable = await runInstaller(mac, harness.installerPath, recoveryArgs, {
...harness.env,
TEST_FAIL_RECOVERY_XATTR_READ: "1",
});
expect(unreadable.status).toBe(1);
expect(unreadable.stderr).toContain(
"could not inspect the recovery app transaction binding",
);
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.json"))).toBe(true);
const resumed = await runInstaller(mac, harness.installerPath, recoveryArgs, harness.env);
expect(resumed.status, resumed.stderr).toBe(0);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"rejects a symlinked current app before recovery mutation",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { launchdBootstrapFails: false });
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const receiptContents = readFileSync(installReceiptPath, "utf8");
const backupPath = (JSON.parse(receiptContents) as { backupPath: string }).backupPath;
rmSync(harness.appPath, { recursive: true });
symlinkSync(backupPath, harness.appPath);
const recovered = await runAuthenticatedElevationRecovery(mac, harness);
expect(recovered.status).toBe(1);
expect(recovered.stderr).toContain("current OpenClaw app has an unsupported entry type");
expect(readFileSync(installReceiptPath, "utf8")).toBe(receiptContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-loaded");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"places a damaged current app into evidence custody before restoring its backup",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { launchdBootstrapFails: false });
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
rmSync(path.join(harness.appPath, "Contents", "Info.plist"));
const recovered = await runAuthenticatedElevationRecovery(mac, harness);
expect(recovered.status, recovered.stderr).toBe(0);
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(
readdirSync(harness.env.HOME).some((name) =>
name.startsWith("InstalledOpenClaw.app.failed-elevation-host-"),
),
).toBe(true);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"reports damaged-app custody when the canonical path is concurrently recreated",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
launchdBootstrapFails: false,
recreateAppDuringDamagedCustody: true,
});
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const receiptContents = readFileSync(installReceiptPath, "utf8");
rmSync(path.join(harness.appPath, "Contents", "Info.plist"));
const recovered = await runAuthenticatedElevationRecovery(mac, harness);
expect(recovered.status).toBe(1);
expect(recovered.stderr).toContain("Preserved damaged current app at");
expect(existsSync(path.join(harness.appPath, "Contents", "replacement"))).toBe(true);
expect(readFileSync(installReceiptPath, "utf8")).toBe(receiptContents);
expect(
readdirSync(harness.env.HOME).some((name) =>
name.startsWith("InstalledOpenClaw.app.failed-elevation-host-"),
),
).toBe(true);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"rejects a same-type app replacement before damaged-app custody",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
launchdBootstrapFails: false,
replaceDamagedAppDirectoryBeforeCustody: true,
});
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const receiptContents = readFileSync(installReceiptPath, "utf8");
const backupPath = (JSON.parse(receiptContents) as { backupPath: string }).backupPath;
rmSync(path.join(harness.appPath, "Contents", "Info.plist"));
const recovered = await runAuthenticatedElevationRecovery(mac, harness);
expect(recovered.status).toBe(1);
expect(recovered.stderr).toContain("Restored replacement app entry at");
expect(recovered.stderr).toContain(
"recovery failed and the current OpenClaw installation could not be restored completely",
);
expect(readFileSync(path.join(harness.appPath, "Contents", "replacement"), "utf8")).toBe(
"replacement-directory\n",
);
expect(existsSync(`${harness.appPath}.race-displaced`)).toBe(true);
expect(existsSync(backupPath)).toBe(true);
expect(readFileSync(installReceiptPath, "utf8")).toBe(receiptContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-absent");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"restores a non-directory replacement moved into damaged-app custody",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
launchdBootstrapFails: false,
symlinkDamagedAppBeforeCustody: true,
});
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const receiptContents = readFileSync(installReceiptPath, "utf8");
rmSync(path.join(harness.appPath, "Contents", "Info.plist"));
const recovered = await runAuthenticatedElevationRecovery(mac, harness);
expect(recovered.status).toBe(1);
expect(lstatSync(harness.appPath).isSymbolicLink()).toBe(true);
expect(readFileSync(installReceiptPath, "utf8")).toBe(receiptContents);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"reverses a failed recovery back to an initially missing current app",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
launchdBootstrapFails: false,
migrationRestoreBootstrapFails: true,
});
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const receiptContents = readFileSync(installReceiptPath, "utf8");
const backupPath = (JSON.parse(receiptContents) as { backupPath: string }).backupPath;
rmSync(harness.appPath, { recursive: true });
const recovered = await runAuthenticatedElevationRecovery(mac, harness);
expect(recovered.status).toBe(1);
expect(existsSync(harness.appPath)).toBe(false);
expect(existsSync(backupPath)).toBe(true);
expect(readFileSync(installReceiptPath, "utf8")).toBe(receiptContents);
expect(
readdirSync(path.dirname(harness.sourcePlist)).some((name) =>
name.startsWith(`${path.basename(harness.sourcePlist)}.reversal-custody.`),
),
).toBe(true);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"preserves a replacement migration owner during failed-recovery reversal",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
launchdBootstrapFails: false,
migrationRestoreBootstrapFails: true,
replaceMigrationSourceDuringReversalCustody: true,
});
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const receiptContents = readFileSync(installReceiptPath, "utf8");
const currentBinary = readFileSync(
path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"),
);
const recovered = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(recovered.status).toBe(1);
expect(recovered.stderr).toContain("Preserved reversed migration plist at");
expect(readFileSync(harness.sourcePlist, "utf8")).toBe("replacement-owner\n");
expect(readFileSync(installReceiptPath, "utf8")).toBe(receiptContents);
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
currentBinary,
);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"rejects a same-content migration replacement before reversal custody",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
launchdBootstrapFails: false,
migrationRestoreBootstrapFails: true,
replaceMigrationSourceSameContentBeforeCustody: true,
});
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const receiptContents = readFileSync(installReceiptPath, "utf8");
const currentBinary = readFileSync(
path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"),
);
const recovered = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
const displacedSource = `${harness.sourcePlist}.race-displaced`;
expect(recovered.status).toBe(1);
expect(recovered.stderr).toContain("Restored unexpected reversal entry at");
expect(recovered.stderr).toContain(
"recovery failed and the current OpenClaw installation could not be restored completely",
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(displacedSource, "utf8")).toBe(harness.sourceContents);
expect(lstatSync(harness.sourcePlist).ino).not.toBe(lstatSync(displacedSource).ino);
expect(readFileSync(installReceiptPath, "utf8")).toBe(receiptContents);
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
currentBinary,
);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-absent");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"restores a symlink replacement moved during migration reversal custody",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
launchdBootstrapFails: false,
migrationRestoreBootstrapFails: true,
symlinkMigrationSourceDuringReversalCustody: true,
});
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const receiptContents = readFileSync(installReceiptPath, "utf8");
const recovered = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(recovered.status).toBe(1);
expect(lstatSync(harness.sourcePlist).isSymbolicLink()).toBe(true);
expect(readFileSync(installReceiptPath, "utf8")).toBe(receiptContents);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"refuses recovery before mutation when process inspection tools are unavailable",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { launchdBootstrapFails: false });
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const binDir = path.join(harness.env.HOME, "bin");
rmSync(path.join(binDir, "lsof"));
const jqPath = (
await mac.run("bash", ["-lc", "command -v jq"], {
encoding: "utf8",
})
).stdout.trim();
symlinkSync(jqPath, path.join(binDir, "jq"));
writeExecutable(path.join(binDir, "diskutil"), "#!/bin/sh\nexit 0\n");
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const recovered = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
{
...harness.env,
PATH: `${binDir}:/usr/bin:/bin:/sbin`,
},
);
expect(recovered.status).toBe(1);
expect(recovered.stderr).toContain("required tool not found: lsof");
expect(existsSync(installReceiptPath)).toBe(true);
expect(existsSync(harness.sourcePlist)).toBe(false);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-loaded");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"preserves recovery custody across uninstall until explicit recovery",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { launchdBootstrapFails: false });
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const uninstalled = await runInstaller(
mac,
harness.installerPath,
["uninstall", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(uninstalled.status, uninstalled.stderr).toBe(0);
expect(uninstalled.stdout).toContain("recovery receipt preserved");
expect(existsSync(installReceiptPath)).toBe(true);
const recovered = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(recovered.status, recovered.stderr).toBe(0);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"recovers across simulated native and Rosetta host selection using both architecture CDHashes",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { launchdBootstrapFails: false });
const installed = await runAuthenticatedMigrationInstall(mac, harness, {
...harness.env,
TEST_NATIVE_ARCH: "arm64",
});
expect(installed.status, installed.stderr).toBe(0);
const receipt = JSON.parse(
readFileSync(path.join(harness.stateDir, "elevation-host-install.json"), "utf8"),
) as {
backupCDHashes: { arm64: string; x86_64: string };
cdhashes: { arm64: string; x86_64: string };
schemaVersion: number;
};
expect(receipt).toMatchObject({
schemaVersion: 3,
transactionState: "installed",
backupCDHashes: {
arm64: "OLDFIXTURECDHASHARM64",
x86_64: "OLDFIXTURECDHASHX8664",
},
cdhashes: {
arm64: "FIXTURECDHASHARM64",
x86_64: "FIXTURECDHASHX8664",
},
});
expect(receipt).not.toHaveProperty("cdhash");
expect(receipt).not.toHaveProperty("backupCDHash");
const recovered = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
{ ...harness.env, TEST_NATIVE_ARCH: "x86_64" },
);
expect(recovered.status, recovered.stderr).toBe(0);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"defers termination until explicit recovery commits one complete generation",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
launchdBootstrapFails: false,
signalDuringRecoveryAppMove: true,
});
const oldBinary = readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"));
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const recovered = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(recovered.signal).toBe("SIGTERM");
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
oldBinary,
);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
expect(existsSync(path.join(harness.stateDir, "elevation-host-install.json"))).toBe(false);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"recovers a migrated source owner when no prior app existed",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { launchdBootstrapFails: false });
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const receipt = JSON.parse(readFileSync(installReceiptPath, "utf8")) as {
backupCDHashes: { arm64: string; x86_64: string };
backupPath: string;
};
rmSync(receipt.backupPath, { recursive: true });
receipt.backupPath = "";
receipt.backupCDHashes = { arm64: "", x86_64: "" };
writeFileSync(installReceiptPath, JSON.stringify(receipt), "utf8");
const recovered = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(recovered.status, recovered.stderr).toBe(0);
expect(existsSync(harness.appPath)).toBe(false);
expect(readFileSync(harness.sourcePlist, "utf8")).toBe(harness.sourceContents);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("source-loaded");
expect(existsSync(installReceiptPath)).toBe(false);
expect(recovered.stdout).toContain("replaced app preserved at");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"restores the current generation when explicit recovery cannot restart the prior owner",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
launchdBootstrapFails: false,
migrationRestoreBootstrapFails: true,
});
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const currentReceipt = readFileSync(installReceiptPath, "utf8");
const currentBinary = readFileSync(
path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"),
);
const rollbackPath = (JSON.parse(currentReceipt) as { backupPath: string }).backupPath;
const recovered = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(recovered.status).toBe(1);
expect(recovered.stderr).toContain(
"could not restore the previous OpenClaw installation completely",
);
expect(readFileSync(installReceiptPath, "utf8")).toBe(currentReceipt);
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
currentBinary,
);
expect(existsSync(rollbackPath)).toBe(true);
expect(existsSync(harness.sourcePlist)).toBe(false);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-loaded");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"never replaces the live receipt with a failed reversal staging copy",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, {
failCurrentReceiptRestoreCopy: true,
launchdBootstrapFails: false,
migrationRestoreBootstrapFails: true,
});
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const currentReceipt = readFileSync(installReceiptPath, "utf8");
const recovered = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(recovered.status).toBe(1);
expect(recovered.stderr).toContain(
"current OpenClaw installation could not be restored completely",
);
expect(readFileSync(installReceiptPath, "utf8")).toBe(currentReceipt);
expect(readFileSync(installReceiptPath, "utf8")).not.toContain("partial");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"refuses recovery before mutation when the recorded app backup is missing",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { launchdBootstrapFails: false });
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const currentReceipt = readFileSync(installReceiptPath, "utf8");
const currentBinary = readFileSync(
path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"),
);
const rollbackPath = (JSON.parse(currentReceipt) as { backupPath: string }).backupPath;
rmSync(rollbackPath, { recursive: true });
const recovered = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(recovered.status).toBe(1);
expect(recovered.stderr).toContain(
"receipt app backup is missing, symlinked, or not a bundle directory",
);
expect(readFileSync(installReceiptPath, "utf8")).toBe(currentReceipt);
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
currentBinary,
);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-loaded");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"refuses recovery before mutation when the app backup signature is invalid",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { launchdBootstrapFails: false });
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const currentReceipt = readFileSync(installReceiptPath, "utf8");
const currentBinary = readFileSync(
path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"),
);
const rollbackPath = (JSON.parse(currentReceipt) as { backupPath: string }).backupPath;
writeFileSync(
path.join(rollbackPath, "Contents", "invalid-signature"),
"invalid\n",
"utf8",
);
const recovered = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(recovered.status).toBe(1);
expect(recovered.stderr).toContain(
"receipt app backup does not pass strict signature and identity validation",
);
expect(readFileSync(installReceiptPath, "utf8")).toBe(currentReceipt);
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
currentBinary,
);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-loaded");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"refuses a corrupt migration backup before stopping the current generation",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { launchdBootstrapFails: false });
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const currentReceipt = readFileSync(installReceiptPath, "utf8");
const receipt = JSON.parse(currentReceipt) as { migration: { backupPlist: string } };
const currentBinary = readFileSync(
path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"),
);
writeFileSync(receipt.migration.backupPlist, "corrupt\n", "utf8");
const recovered = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(recovered.status).toBe(1);
expect(recovered.stderr).toContain("migration plist backup failed digest validation");
expect(readFileSync(installReceiptPath, "utf8")).toBe(currentReceipt);
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
currentBinary,
);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-loaded");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"preserves an origin-main legacy receipt across upgrade recovery and reinstall",
async ({ mac }) =>
mac.lifetime.run(async () => {
const script = readFileSync(scriptPath, "utf8");
expect(script).toContain(
'receipt_restore_tmp="$(mktemp "$STATE_DIR/elevation-host.restore-receipt.${ROLLBACK_FAILED_SOURCE}.XXXXXX")"',
);
expect(script).not.toContain('receipt_restore_tmp="${RECEIPT_PATH}.restore.$$"');
const harness = createInstallRollbackHarness(mac, { launchdBootstrapFails: false });
const originalBinary = readFileSync(
path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"),
);
const firstInstall = await runAuthenticatedMigrationInstall(mac, harness);
expect(firstInstall.status, firstInstall.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const currentReceipt = JSON.parse(readFileSync(installReceiptPath, "utf8")) as Record<
string,
unknown
>;
const legacyBackupPath = String(currentReceipt.backupPath).replace(
/[.][A-Za-z0-9]{6}$/,
"",
);
renameSync(String(currentReceipt.backupPath), legacyBackupPath);
const legacyReceipt = {
sourceCommit: currentReceipt.sourceCommit,
peekabooCommit: currentReceipt.peekabooCommit,
archiveSha256: currentReceipt.archiveSha256,
appPath: currentReceipt.appPath,
backupPath: legacyBackupPath,
plistPath: currentReceipt.plistPath,
previousPlist: currentReceipt.previousPlist,
};
writeFileSync(installReceiptPath, JSON.stringify(legacyReceipt), "utf8");
const firstReceipt = readFileSync(installReceiptPath, "utf8");
const managedInstallArgs = [
"install",
"--archive",
harness.archivePath,
"--receipt",
harness.receiptPath,
...receiptDigestArgs(harness.receiptPath),
"--app",
harness.appPath,
"--state-dir",
harness.stateDir,
"--config-path",
harness.configPath,
];
const upgrade = await runInstaller(
mac,
harness.installerPath,
managedInstallArgs,
harness.env,
);
expect(upgrade.status, upgrade.stderr).toBe(0);
const upgradeReceipt = JSON.parse(readFileSync(installReceiptPath, "utf8")) as {
previousReceipt: string;
previousReceiptSha256: string;
};
expect(upgradeReceipt.previousReceipt).toContain("elevation-host.previous-receipt.");
expect(upgradeReceipt.previousReceiptSha256).toBe(sha256(firstReceipt));
const recovered = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(recovered.status, recovered.stderr).toBe(0);
expect(readFileSync(installReceiptPath, "utf8")).toBe(firstReceipt);
expect(recovered.stdout).toContain("replaced app preserved at");
const legacyStatus = await runInstaller(
mac,
harness.installerPath,
["status", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(legacyStatus.status, legacyStatus.stderr).toBe(0);
expect(legacyStatus.stdout).toContain("Elevation host ready");
writeExecutable(
path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"),
"#!/bin/sh\nexit 0\n",
);
const unauthenticatedRecovery = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(unauthenticatedRecovery.status).toBe(1);
expect(unauthenticatedRecovery.stderr).toContain(
"recovery requires the authenticated elevation archive",
);
expect(readFileSync(installReceiptPath, "utf8")).toBe(firstReceipt);
const legacyRecovery = await runAuthenticatedElevationRecovery(mac, harness);
expect(legacyRecovery.status, legacyRecovery.stderr).toBe(0);
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
originalBinary,
);
expect(existsSync(installReceiptPath)).toBe(false);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-absent");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"inherits legacy managed-upgrade config from the installed elevation plist",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { launchdBootstrapFails: false });
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const currentReceipt = JSON.parse(readFileSync(installReceiptPath, "utf8")) as Record<
string,
unknown
>;
writeFileSync(
installReceiptPath,
JSON.stringify({
sourceCommit: currentReceipt.sourceCommit,
peekabooCommit: currentReceipt.peekabooCommit,
archiveSha256: currentReceipt.archiveSha256,
appPath: currentReceipt.appPath,
backupPath: currentReceipt.backupPath,
plistPath: currentReceipt.plistPath,
previousPlist: currentReceipt.previousPlist,
}),
"utf8",
);
const customConfig = path.join(harness.stateDir, "custom-openclaw.json");
writeFileSync(customConfig, "{}\n", "utf8");
const elevationPlistPath = path.join(
harness.env.HOME,
"Library",
"LaunchAgents",
"ai.openclaw.mac.elevation-host.plist",
);
writeFileSync(
elevationPlistPath,
readFileSync(elevationPlistPath, "utf8").replace(harness.configPath, customConfig),
"utf8",
);
const mismatched = await runInstaller(
mac,
harness.installerPath,
[
"install",
"--archive",
harness.archivePath,
"--receipt",
harness.receiptPath,
...receiptDigestArgs(harness.receiptPath),
"--app",
harness.appPath,
"--state-dir",
harness.stateDir,
"--config-path",
harness.configPath,
],
harness.env,
);
expect(mismatched.status).toBe(1);
expect(mismatched.stderr).toContain(
"--config-path does not match the existing elevation install receipt",
);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"refuses recovery when another owner recreates the source LaunchAgent path",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { launchdBootstrapFails: false });
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
writeFileSync(harness.sourcePlist, "replacement owner\n", "utf8");
const installedBinary = readFileSync(
path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"),
);
const recovered = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(recovered.status).toBe(1);
expect(recovered.stderr).toContain("could not restore the previous OpenClaw installation");
expect(readFileSync(harness.sourcePlist, "utf8")).toBe("replacement owner\n");
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
installedBinary,
);
expect(readFileSync(harness.launchStateFile, "utf8").trim()).toBe("elevation-loaded");
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"refuses recovery when a dangling symlink recreates the source LaunchAgent path",
async ({ mac }) =>
mac.lifetime.run(async () => {
const script = readFileSync(scriptPath, "utf8");
const restoreBody = script.slice(
script.indexOf("restore_file_without_overwrite()"),
script.indexOf("verify_artifact_receipt()"),
);
expect(restoreBody).toContain('/bin/link "$restore_tmp" "$destination"');
expect(restoreBody).not.toContain('ln "$restore_tmp" "$destination"');
const harness = createInstallRollbackHarness(mac, { launchdBootstrapFails: false });
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installedBinary = readFileSync(
path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"),
);
symlinkSync(path.join(harness.env.HOME, "missing-owner.plist"), harness.sourcePlist);
const recovered = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(recovered.status).toBe(1);
expect(recovered.stderr).toContain("could not restore the previous OpenClaw installation");
expect(readFileSync(path.join(harness.appPath, "Contents", "MacOS", "OpenClaw"))).toEqual(
installedBinary,
);
}),
);
it.skipIf(process.platform !== "darwin").concurrent(
"rejects a receipt backup path that lexically escapes the canonical state directory",
async ({ mac }) =>
mac.lifetime.run(async () => {
const harness = createInstallRollbackHarness(mac, { launchdBootstrapFails: false });
const installed = await runAuthenticatedMigrationInstall(mac, harness);
expect(installed.status, installed.stderr).toBe(0);
const installReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const installReceipt = JSON.parse(readFileSync(installReceiptPath, "utf8")) as {
migration: { backupPlist: string; backupSha256: string };
};
const deceptiveDirectory = path.join(
harness.stateDir,
`elevation-host.previous-launch-agent.${"a".repeat(40)}.ABCDEF`,
);
mkdirSync(deceptiveDirectory);
const outsideBackup = path.join(harness.env.HOME, "outside-backup.plist");
writeFileSync(outsideBackup, "attacker-selected\n", "utf8");
installReceipt.migration.backupPlist = path.join(
deceptiveDirectory,
"..",
"..",
path.basename(outsideBackup),
);
installReceipt.migration.backupSha256 = sha256(readFileSync(outsideBackup));
writeFileSync(installReceiptPath, JSON.stringify(installReceipt), "utf8");
const recovered = await runInstaller(
mac,
harness.installerPath,
["recover", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(recovered.status).toBe(1);
expect(recovered.stderr).toContain("receipt migration plist backup path is not canonical");
expect(readFileSync(outsideBackup, "utf8")).toBe("attacker-selected\n");
}),
);
it.concurrent("treats missing TCC after a Bridge-ready install as degraded capability", async ({
mac,
}) =>
mac.lifetime.run(async () => {
const script = readFileSync(scriptPath, "utf8");
const installBody = script.slice(
script.indexOf("install_host()"),
script.indexOf("recover_install()"),
);
const statusBody = script.slice(
script.indexOf("status_host()"),
script.indexOf("recover_host()"),
);
expect(installBody).toContain("tcc_summary || true");
expect(statusBody).toContain("tcc_summary || return $?");
}));
it.concurrent("relaunches an adopted app only after exact exit with its selected state and config", async ({
mac,
}) =>
mac.lifetime.run(async () => {
const script = readFileSync(scriptPath, "utf8");
const installBody = script.slice(
script.indexOf("install_host()"),
script.indexOf("recover_install()"),
);
const relaunchBody = script.slice(
script.indexOf("relaunch_adopted_app()"),
script.indexOf("run_openclaw_cli()"),
);
const recoverBody = script.slice(
script.indexOf("recover_install()"),
script.indexOf("status_host()"),
);
const recoverHostBody = script.slice(
script.indexOf("recover_host()"),
script.indexOf("uninstall_host()"),
);
expect(installBody.indexOf("CUTOVER_ADOPTION_STOPPED=1")).toBeLessThan(
installBody.indexOf('kill "$ADOPTION_PID"'),
);
expect(installBody.indexOf("adopted_app_is_current || fail")).toBeLessThan(
installBody.indexOf('kill "$ADOPTION_PID"'),
);
expect(relaunchBody).toContain('--env "OPENCLAW_STATE_DIR=$STATE_DIR"');
expect(relaunchBody).toContain('--env "OPENCLAW_CONFIG_PATH=$CONFIG_PATH"');
expect(relaunchBody).toContain("-g");
expect(relaunchBody).toContain("wait_for_adopted_app_resume");
expect(installBody.indexOf("CUTOVER_ADOPTION_TERMINATION_SENT=1")).toBeGreaterThan(
installBody.indexOf('kill "$ADOPTION_PID"'),
);
expect(installBody.indexOf("CUTOVER_ADOPTION_TERMINATION_SENT=1")).toBeLessThan(
installBody.indexOf("adopted OpenClaw process did not exit"),
);
expect(recoverBody).toContain("restore_adopted_app_after_cutover || recovery_failed=1");
expect(recoverHostBody).toContain('CONFIG_PATH="$(jq -r \'.configPath\' "$RECEIPT_PATH")"');
}));
it.concurrent.for([
["fail", "TCC: unknown (permission probe failed)"],
["invalid", "TCC: unknown (permission probe returned invalid status)"],
] as const)(
"fails closed when the TCC permission probe returns %s output",
async ([mode, diagnostic], { mac }) =>
mac.lifetime.run(async () => {
const harness = createStatusHarness(mac, mode);
const result = await runInstaller(
mac,
scriptPath,
["status", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(result.status, result.stderr).toBe(4);
expect(result.stdout).toContain("Elevation host ready: pid=4242");
expect(result.stdout).toContain(diagnostic);
expect(result.stdout).not.toContain("TCC: ready");
}),
);
it.concurrent("reports an incomplete prepared install instead of a healthy final receipt", async ({
mac,
}) =>
mac.lifetime.run(async () => {
const harness = createStatusHarness(mac, "invalid");
const finalReceiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const pendingReceiptPath = path.join(harness.stateDir, "elevation-host-install.pending.json");
const pendingReceipt = JSON.parse(readFileSync(finalReceiptPath, "utf8")) as {
transactionId: string;
transactionState: string;
};
pendingReceipt.transactionId = "00000000-0000-4000-8000-000000000002";
pendingReceipt.transactionState = "installing";
writeFileSync(pendingReceiptPath, JSON.stringify(pendingReceipt), "utf8");
const result = await runInstaller(
mac,
scriptPath,
["status", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(result.status).toBe(1);
expect(result.stderr).toContain(
"incomplete elevation install transaction exists; run recover",
);
expect(existsSync(pendingReceiptPath)).toBe(true);
}));
it.concurrent.for([1, 2])(
"rejects the unshipped install receipt schema %i",
async (schemaVersion, { mac }) =>
mac.lifetime.run(async () => {
const harness = createStatusHarness(mac, "invalid");
const receiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const receipt = JSON.parse(readFileSync(receiptPath, "utf8")) as Record<string, unknown>;
receipt.schemaVersion = schemaVersion;
writeFileSync(receiptPath, JSON.stringify(receipt), "utf8");
const result = await runInstaller(
mac,
scriptPath,
["status", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(result.status).toBe(1);
expect(result.stderr).toContain("elevation install receipt schema is invalid");
}),
);
it.concurrent("rejects stale single-hash fields and inconsistent backup hash custody", async ({
mac,
}) =>
mac.lifetime.run(async () => {
const harness = createStatusHarness(mac, "invalid");
const receiptPath = path.join(harness.stateDir, "elevation-host-install.json");
const receipt = JSON.parse(readFileSync(receiptPath, "utf8")) as {
backupCDHashes: { arm64: string; x86_64: string };
backupPath: string;
cdhash?: string;
};
receipt.cdhash = "IGNORED-SINGLE-HASH";
writeFileSync(receiptPath, JSON.stringify(receipt), "utf8");
const staleField = await runInstaller(
mac,
scriptPath,
["status", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(staleField.status).toBe(1);
expect(staleField.stderr).toContain("elevation install receipt schema is invalid");
delete receipt.cdhash;
receipt.backupPath = path.join(harness.stateDir, "missing-backup.app");
receipt.backupCDHashes = { arm64: "", x86_64: "" };
writeFileSync(receiptPath, JSON.stringify(receipt), "utf8");
const inconsistentBackup = await runInstaller(
mac,
scriptPath,
["status", "--app", harness.appPath, "--state-dir", harness.stateDir],
harness.env,
);
expect(inconsistentBackup.status).toBe(1);
expect(inconsistentBackup.stderr).toContain("elevation install receipt schema is invalid");
}));
it.concurrent("builds an immutable source-addressed notarized ZIP with a portable installer", async ({
mac,
}) =>
mac.lifetime.run(async () => {
const script = readFileSync(scriptPath, "utf8");
expect(script).toContain(
'prefix="OpenClaw-${source_commit}-Peekaboo-${EXPECTED_PEEKABOO_SOURCE_COMMIT}-stable"',
);
expect(script).toContain("immutable elevation output already exists");
expect(script).toContain("OPENCLAW_MAC_SIGNING_VARIANT=elevation-host");
expect(script).toContain("SKIP_DMG=1");
expect(script).toContain("NOTARY_RESULT_FILE");
expect(script).toContain("archiveSha256");
expect(script).toContain("archiveChecksum");
expect(script).toContain('installer_path="$OUTPUT_DIR/${prefix}-installer.sh"');
expect(script).toContain("installerSha256");
expect(script).toContain("installerChecksum");
expect(script).toContain("openclaw-elevation-artifact");
expect(script).toContain("verify_artifact_receipt");
expect(script).toContain(
'git -C "$ROOT_DIR" show "${source_commit}:scripts/mac-elevation-host.sh"',
);
expect(script).toContain("portable installer does not match the selected source commit");
expect(script).not.toContain("--elevation-installer");
expect(script).toContain("notarizationId");
expect(script).toContain("entitlementsSha256");
expect(script).toContain("codesign --verify --strict --test-requirement='=notarized'");
expect(script).toContain('spctl --assess --type execute "$app"');
}));
it.concurrent("keeps portable verification identity aligned with the signer", async ({ mac }) =>
mac.lifetime.run(async () => {
const portableScript = readFileSync(scriptPath, "utf8");
const codesignScript = readFileSync(codesignScriptPath, "utf8");
const constant = (source: string, name: string) =>
source.match(new RegExp(`^${name}="([^"]+)"$`, "m"))?.[1];
expect(
[
constant(portableScript, "EXPECTED_TEAM_ID"),
constant(portableScript, "EXPECTED_AUTHORITY"),
],
"mac-elevation-host.sh verifies the signed app, so its duplicated signing constants must match codesign-mac-app.sh",
).toEqual([
constant(codesignScript, "ELEVATION_TEAM_ID"),
constant(codesignScript, "ELEVATION_IDENTITY"),
]);
}));
it.skipIf(process.platform !== "darwin").concurrent(
"renders a persistent background-only launchd job without changing normal login",
async ({ mac }) =>
mac.lifetime.run(async () => {
const tempRoot = mac.createTempDir("openclaw-elevation-plist-");
const stateDir = path.join(tempRoot, "state");
const configPath = path.join(stateDir, "openclaw.json");
const appPath = path.join(tempRoot, "OpenClaw.app");
const result = await mac.run(
"bash",
[
scriptPath,
"print-plist",
"--app",
appPath,
"--state-dir",
stateDir,
"--config-path",
configPath,
],
{
cwd: process.cwd(),
encoding: "utf8",
env: { ...process.env, HOME: tempRoot, TMPDIR: tempRoot },
},
);
expect(result.status, result.stderr).toBe(0);
const plistPath = path.join(tempRoot, "rendered.plist");
writeFileSync(plistPath, result.stdout, "utf8");
const json = await mac.run("plutil", ["-convert", "json", "-o", "-", plistPath], {
encoding: "utf8",
});
expect(json.status, json.stderr).toBe(0);
const plist = JSON.parse(json.stdout) as Record<string, unknown>;
expect(plist.Label).toBe("ai.openclaw.mac.elevation-host");
expect(plist.ProgramArguments).toEqual([
`${appPath}/Contents/MacOS/OpenClaw`,
"--elevation-host",
]);
expect(plist.RunAtLoad).toBe(true);
expect(plist.KeepAlive).toBe(true);
expect(plist.EnvironmentVariables).toMatchObject({
OPENCLAW_CONFIG_PATH: configPath,
OPENCLAW_STATE_DIR: stateDir,
});
}),
);
it.concurrent("rejects non-absolute state paths before probing host tools", async ({ mac }) =>
mac.lifetime.run(async () => {
const tempRoot = mac.createTempDir("openclaw-elevation-input-");
const result = await runInstaller(
mac,
scriptPath,
["status", "--state-dir", "relative/state"],
{
...process.env,
HOME: tempRoot,
},
);
expect(result.status).toBe(1);
expect(result.stderr.trim()).toBe("ERROR: --state-dir must be absolute");
}));
});