Files
edgeever/scripts/verify-acp-adapter-real-e2e.mjs
tianma-if 59d6548abd fix: harden remaining dependency paths and desktop npm
Remove the unused React Native Markdown renderer. Pin safe YAML, shell quoting, tar, and cache dependencies; upgrade npm within the 11 series and apply upstream fixes to its hidden bundled Undici and cache copies.

Add actual-consumer security regressions and an opt-in real npm/ACP upgrade verifier. Typechecks, Web build, Android APK/signature checks, and 2,997 tests passed (23 skipped). Real old-to-new npm installs, ACP initialize, rollback, restart, and fallback retention passed under the desktop Electron runtime.

Android USB-device validation and full desktop automatic-update release gates remain pending. Dependency audit is reduced from 85 to 74 unique advisories; this is not a complete security clearance.
2026-10-12 12:48:47 +08:00

463 lines
19 KiB
JavaScript

/**
* ACP Adapter Installer Real End-to-End Cross-Version Verification (Linux x86_64)
*
* Exercises an authentic old-to-new upgrade lifecycle using official Google Antigravity
* binary distributions:
* - Antigravity v1.2.1 (333,590,110 bytes archive, ~1.05 GB uncompressed)
* - Antigravity v1.3.0 (333,727,150 bytes archive, ~1.05 GB uncompressed)
*
* Verifies:
* 1. Fresh installation of v1.2.1 with real archive extraction and live stdio ACP handshake.
* 2. Real rollback protection when an update to v1.3.0 fails validation, confirming that
* v1.2.1 remains intact and operable, and temporary staging directories are unlinked.
* 3. Real cross-version upgrade to v1.3.0 with live ACP handshake validation, atomic swap,
* manifest persistence, and immediate fallback retention (v1.2.1 kept on disk).
* 4. Version pruning lifecycle, confirming retention of active and fallback versions.
* 5. Official archives exceed the former 300 MiB download limit; extracted payloads remain
* below the former 1 GiB limit, with the 2 GiB limit providing room for future releases.
*/
import assert from "node:assert/strict";
import { spawn } from "node:child_process";
import { createHash } from "node:crypto";
import { createReadStream, existsSync, mkdtempSync, statSync } from "node:fs";
import { mkdir, open, readFile, readdir, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { Readable } from "node:stream";
import { createAcpAdapterManager } from "../apps/desktop/src/main/acp-adapter-manager.mjs";
const OFFICIAL_RELEASES = {
"1.2.1": {
id: "antigravity-acp",
version: "1.2.1",
distribution: {
binary: {
"linux-x86_64": {
archive: "https://dl.google.com/agy-extensions/releases/linux/agy-acp-server-1.2.1-linux-x86_64.zip",
cmd: "./agy_acp_server.par",
args: ["--uid="],
},
},
},
},
"1.3.0": {
id: "antigravity-acp",
version: "1.3.0",
distribution: {
binary: {
"linux-x86_64": {
archive: "https://dl.google.com/agy-extensions/releases/linux/agy-acp-server-1.3.0-linux-x86_64.zip",
cmd: "./agy_acp_server.par",
args: ["--uid="],
},
},
},
},
};
/**
* Executes a real ACP protocol handshake over stdio against an agy_acp_server binary.
*/
export async function executeRealAcpHandshake(commandSpec, { timeoutMs = 25_000 } = {}) {
const binaryPath = commandSpec.command;
const args = commandSpec.args ?? [];
if (!existsSync(binaryPath)) {
throw new Error(`binary_not_found: ${binaryPath}`);
}
const child = spawn(binaryPath, args, {
env: { ...process.env, ...commandSpec.env },
stdio: ["pipe", "pipe", "ignore"],
windowsHide: true,
shell: false,
});
return new Promise((resolve, reject) => {
let resolved = false;
let stdoutBuffer = "";
const timer = setTimeout(() => {
cleanup(new Error(`acp_handshake_timeout (${timeoutMs}ms)`));
}, timeoutMs);
timer.unref?.();
function cleanup(error) {
if (resolved) return;
resolved = true;
clearTimeout(timer);
try { child.stdin?.destroy(); } catch {}
try { child.kill("SIGTERM"); } catch {}
const killTimer = setTimeout(() => {
try { child.kill("SIGKILL"); } catch {}
}, 1000);
killTimer.unref?.();
if (error) reject(error);
}
child.on("error", (err) => cleanup(err));
child.on("exit", (code) => {
if (!resolved) {
cleanup(new Error(`process_exited_prematurely (code=${code})`));
}
});
child.stdout.on("data", (chunk) => {
stdoutBuffer += chunk.toString();
const lines = stdoutBuffer.split("\n");
stdoutBuffer = lines.pop() ?? "";
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed) continue;
try {
const message = JSON.parse(trimmed);
if (message.id === 1 && message.result) {
resolved = true;
clearTimeout(timer);
try { child.stdin?.destroy(); } catch {}
try { child.kill("SIGTERM"); } catch {}
resolve({
state: "available",
protocolVersion: message.result.protocolVersion,
promptCapabilities: message.result.agentCapabilities?.promptCapabilities ?? {},
authMethods: message.result.authMethods ?? [],
rawResult: message.result,
});
return;
}
} catch {
// Non-JSON or partial line, keep reading
}
}
});
// Send ACP initialize request
const initRequest = JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "initialize",
params: {
protocolVersion: 1,
clientCapabilities: {
fs: { readTextFile: false, writeTextFile: false },
terminal: false,
},
clientInfo: { name: "edgeever-e2e-verifier", version: "1.100.0" },
},
}) + "\n";
child.stdin.write(initRequest);
});
}
/**
* Downloads a file to destination if not already present, computing SHA-256 and size.
*/
async function ensureArtifactCached(url, cachePath, { log = console.log } = {}) {
await mkdir(path.dirname(cachePath), { recursive: true });
if (existsSync(cachePath)) {
const stat = statSync(cachePath);
if (stat.size > 100 * 1024 * 1024) {
log(` Using cached artifact: ${path.basename(cachePath)} (${(stat.size / 1024 / 1024).toFixed(2)} MiB)`);
return stat.size;
}
}
log(` Downloading ${url} -> ${cachePath}...`);
const response = await fetch(url);
if (!response.ok || !response.body) {
throw new Error(`Failed to download ${url}: HTTP ${response.status}`);
}
const handle = await open(cachePath, "w", 0o600);
let bytesWritten = 0;
try {
for await (const chunk of Readable.fromWeb(response.body)) {
await handle.write(chunk, 0, chunk.length);
bytesWritten += chunk.length;
}
} finally {
await handle.close();
}
log(` Downloaded ${path.basename(cachePath)}: ${(bytesWritten / 1024 / 1024).toFixed(2)} MiB`);
return bytesWritten;
}
/**
* Calculates SHA-256 digest of a file.
*/
async function computeSha256(filePath) {
const hash = createHash("sha256");
for await (const chunk of createReadStream(filePath)) {
hash.update(chunk);
}
return hash.digest("hex");
}
/**
* Runs the full real Linux E2E verification workflow.
*/
export async function runRealAcpAdapterCrossVersionVerification({
cacheDir = path.join(tmpdir(), "edgeever-e2e-cache"),
log = console.log,
} = {}) {
assert.equal(process.platform, "linux", "Verification requires Linux host");
assert.equal(process.arch, "x64", "Verification requires x86_64 CPU architecture");
const startTime = Date.now();
const root = mkdtempSync(path.join(tmpdir(), "edgeever-acp-real-e2e-"));
const record = {
platform: "linux",
arch: "x64",
storeRoot: root,
cacheDir,
phases: [],
artifacts: {},
};
log("=================================================================");
log("ACP Adapter Installer Real Linux Cross-Version Verification");
log("=================================================================");
log(`Store Root: ${root}`);
log(`Cache Dir: ${cacheDir}\n`);
try {
// Step 0: Ensure official artifacts are downloaded
log("[0/4] Downloading / verifying official distribution archives from Google CDN...");
const url121 = OFFICIAL_RELEASES["1.2.1"].distribution.binary["linux-x86_64"].archive;
const url130 = OFFICIAL_RELEASES["1.3.0"].distribution.binary["linux-x86_64"].archive;
const cache121 = path.join(cacheDir, "agy-acp-server-1.2.1-linux-x86_64.zip");
const cache130 = path.join(cacheDir, "agy-acp-server-1.3.0-linux-x86_64.zip");
const size121 = await ensureArtifactCached(url121, cache121, { log });
const size130 = await ensureArtifactCached(url130, cache130, { log });
const sha121 = await computeSha256(cache121);
const sha130 = await computeSha256(cache130);
record.artifacts["1.2.1"] = { url: url121, sizeBytes: size121, sha256: sha121 };
record.artifacts["1.3.0"] = { url: url130, sizeBytes: size130, sha256: sha130 };
log(` Artifact v1.2.1: ${size121} bytes (~${(size121 / 1024 / 1024).toFixed(2)} MiB), SHA256: ${sha121}`);
log(` Artifact v1.3.0: ${size130} bytes (~${(size130 / 1024 / 1024).toFixed(2)} MiB), SHA256: ${sha130}\n`);
// Setup custom fetchImpl that streams the real cached zip bytes and serves registry entries
let activeRegistryVersion = "1.2.1";
const customFetch = async (url) => {
if (url.endsWith(".zip")) {
const is121 = url.includes("1.2.1");
const filePath = is121 ? cache121 : cache130;
const fileSize = is121 ? size121 : size130;
const nodeStream = createReadStream(filePath);
return new Response(Readable.toWeb(nodeStream), {
status: 200,
headers: { "content-length": String(fileSize) },
});
}
return new Response(JSON.stringify({ agents: [OFFICIAL_RELEASES[activeRegistryVersion]] }), {
status: 200,
});
};
const manager = createAcpAdapterManager({
root,
platform: "linux",
arch: "x64",
fetchImpl: customFetch,
});
const versionRoot = path.join(root, "antigravity");
// Phase 1: Real Baseline Install (v1.2.1)
log("[1/4] Phase 1: Installing official Antigravity v1.2.1 distribution...");
let p1ValidationPayload = null;
const install121Result = await manager.install("antigravity", async (command) => {
log(` Validating staged v1.2.1 command: ${command.command} ${command.args.join(" ")}`);
assert.ok(command.command.includes(".stage-1.2.1"), "Staged binary path must be inspected during validation");
assert.deepEqual(command.args, ["--uid="], "Linux arguments must include --uid=");
const handshake = await executeRealAcpHandshake(command);
log(` Real ACP handshake on staged v1.2.1 SUCCESS! Capabilities:`, JSON.stringify(handshake.promptCapabilities));
p1ValidationPayload = handshake;
return handshake;
});
assert.equal(install121Result.updated, true, "v1.2.1 install must report updated: true");
assert.equal(install121Result.version, "1.2.1", "Installed version must be 1.2.1");
const installed121 = manager.get("antigravity");
assert.ok(installed121, "Manager must return installed antigravity entry");
assert.equal(installed121.version, "1.2.1");
const binary121 = path.join(versionRoot, "1.2.1", "agy_acp_server.par");
assert.equal(installed121.command.command, binary121);
assert.ok(existsSync(binary121), "v1.2.1 binary must exist on disk");
const statBinary121 = statSync(binary121);
const statHarness121 = statSync(path.join(versionRoot, "1.2.1", "localharness_external"));
const totalExtracted121 = statBinary121.size + statHarness121.size;
log(` v1.2.1 files extracted: agy_acp_server.par (${(statBinary121.size / 1024 / 1024).toFixed(2)} MiB), localharness_external (${(statHarness121.size / 1024 / 1024).toFixed(2)} MiB)`);
log(` Total extracted bytes: ${totalExtracted121} bytes (~${(totalExtracted121 / 1024 / 1024 / 1024).toFixed(2)} GiB, below the former 1 GiB cap)`);
// Verify ACP handshake against committed install path
const liveHandshake121 = await executeRealAcpHandshake(installed121.command);
assert.equal(liveHandshake121.state, "available");
log(` Verified committed v1.2.1 ACP handshake operational.\n`);
record.phases.push({
phase: 1,
name: "Fresh Install v1.2.1",
version: "1.2.1",
extractedBytes: totalExtracted121,
handshake: p1ValidationPayload,
});
// Phase 2: Failed Upgrade to v1.3.0 & Rollback Verification
log("[2/4] Phase 2: Simulating failed upgrade to v1.3.0 and verifying rollback behavior...");
activeRegistryVersion = "1.3.0";
let updateFailedThrew = false;
let stageDirObserved = null;
try {
await manager.install("antigravity", async (command) => {
log(` Validation callback triggered for staged v1.3.0: ${command.command}`);
assert.ok(command.command.includes(".stage-1.3.0"), "Must stage v1.3.0 binary before commit");
stageDirObserved = path.dirname(command.command);
assert.ok(existsSync(stageDirObserved), "Stage directory must exist during validation");
// Verify staged files are real 1.3.0 binaries
const statStaged = statSync(command.command);
assert.ok(statStaged.size > 800 * 1024 * 1024, "Staged binary must be real >800 MiB binary");
// Intentionally reject validation to test rollback
log(` Intentionally rejecting validation to test atomic rollback...`);
return { state: "failed", detail: "simulated_handshake_rejection" };
}, { updateOnly: true });
} catch (error) {
updateFailedThrew = true;
assert.equal(error?.message, "simulated_handshake_rejection");
}
assert.ok(updateFailedThrew, "manager.install must reject when validation fails");
// Verify rollback guarantees
const managerStateAfterFailure = manager.get("antigravity");
assert.equal(managerStateAfterFailure?.version, "1.2.1", "Manager must retain active version 1.2.1");
assert.equal(managerStateAfterFailure?.command.command, binary121, "Active command must point to v1.2.1");
const manifestAfterFailure = JSON.parse(await readFile(path.join(root, "installed.json"), "utf8"));
assert.equal(manifestAfterFailure.antigravity?.version, "1.2.1", "installed.json must preserve v1.2.1");
assert.ok(existsSync(binary121), "v1.2.1 binary must remain intact");
// Verify staged directory was cleanly purged
const versionDirContents = await readdir(versionRoot);
const lingeringStages = versionDirContents.filter((name) => name.startsWith(".stage-"));
assert.equal(lingeringStages.length, 0, `No staging directories must remain, found: ${lingeringStages.join(", ")}`);
// Verify that the original v1.2.1 adapter still performs live ACP handshake
const fallbackHandshake = await executeRealAcpHandshake(managerStateAfterFailure.command);
assert.equal(fallbackHandshake.state, "available", "v1.2.1 adapter must remain fully functional after aborted update");
log(` Rollback protection verified: v1.2.1 retained, staging artifacts purged, and adapter functional.\n`);
record.phases.push({
phase: 2,
name: "Failed Upgrade Rollback",
targetVersion: "1.3.0",
retainedVersion: "1.2.1",
stagePurged: true,
fallbackHandshakeSuccess: true,
});
// Phase 3: Successful Cross-Version Upgrade to v1.3.0
log("[3/4] Phase 3: Executing successful cross-version upgrade to official Antigravity v1.3.0...");
let p3ValidationPayload = null;
const update130Result = await manager.install("antigravity", async (command) => {
log(` Validating staged v1.3.0 command: ${command.command}`);
assert.ok(command.command.includes(".stage-1.3.0"));
assert.deepEqual(command.args, ["--uid="]);
const handshake = await executeRealAcpHandshake(command);
log(` Real ACP handshake on staged v1.3.0 SUCCESS! Protocol Version: ${handshake.protocolVersion}, Capabilities:`, JSON.stringify(handshake.promptCapabilities));
p3ValidationPayload = handshake;
return handshake;
}, { updateOnly: true });
assert.equal(update130Result.updated, true, "Update must report updated: true");
assert.equal(update130Result.version, "1.3.0", "Update version must be 1.3.0");
const installed130 = manager.get("antigravity");
assert.equal(installed130?.version, "1.3.0", "Active version must now be 1.3.0");
const binary130 = path.join(versionRoot, "1.3.0", "agy_acp_server.par");
assert.equal(installed130?.command.command, binary130, "Active command must point to 1.3.0 binary");
assert.ok(existsSync(binary130), "v1.3.0 binary must exist on disk");
const statBinary130 = statSync(binary130);
const statHarness130 = statSync(path.join(versionRoot, "1.3.0", "localharness_external"));
const totalExtracted130 = statBinary130.size + statHarness130.size;
log(` v1.3.0 files extracted: agy_acp_server.par (${(statBinary130.size / 1024 / 1024).toFixed(2)} MiB), localharness_external (${(statHarness130.size / 1024 / 1024).toFixed(2)} MiB)`);
log(` Total extracted bytes: ${totalExtracted130} bytes (~${(totalExtracted130 / 1024 / 1024 / 1024).toFixed(2)} GiB)`);
// Verify installed.json
const manifestUpdated = JSON.parse(await readFile(path.join(root, "installed.json"), "utf8"));
assert.equal(manifestUpdated.antigravity?.version, "1.3.0", "installed.json must record v1.3.0");
// Verify immediate fallback retention: v1.2.1 directory must still be intact on disk
assert.ok(existsSync(binary121), "Previous v1.2.1 binary must be retained alongside v1.3.0 as immediate rollback fallback");
// Verify live ACP handshake against updated v1.3.0 adapter
const liveHandshake130 = await executeRealAcpHandshake(installed130.command);
assert.equal(liveHandshake130.state, "available");
log(` Verified committed v1.3.0 ACP handshake operational.\n`);
record.phases.push({
phase: 3,
name: "Successful Upgrade v1.3.0",
version: "1.3.0",
extractedBytes: totalExtracted130,
fallbackRetained: true,
handshake: p3ValidationPayload,
});
// Phase 4: Prune Lifecycle & Cleanup
log("[4/4] Phase 4: Verifying prune lifecycle and cleanup...");
await manager.prune();
assert.ok(existsSync(path.join(versionRoot, "1.3.0")), "Active v1.3.0 version must be kept");
assert.ok(existsSync(path.join(versionRoot, "1.2.1")), "Immediate previous v1.2.1 fallback must be kept");
const postPruneStages = (await readdir(versionRoot)).filter((name) => name.startsWith(".stage-"));
assert.equal(postPruneStages.length, 0, "No stage directories after prune");
// Final live probe
const finalHandshake = await executeRealAcpHandshake(manager.get("antigravity").command);
assert.equal(finalHandshake.state, "available");
log(` Pruning complete: v1.3.0 and v1.2.1 retained, stage clean, adapter verified.\n`);
const elapsedMs = Date.now() - startTime;
log("=================================================================");
log(`ACP Adapter Real Linux Cross-Version Verification: PASSED (${(elapsedMs / 1000).toFixed(2)}s)`);
log("=================================================================");
record.elapsedMs = elapsedMs;
record.success = true;
return record;
} finally {
log(`Cleaning up test root: ${root}`);
await rm(root, { recursive: true, force: true });
}
}
if (import.meta.main || process.argv[1] === new URL(import.meta.url).pathname) {
runRealAcpAdapterCrossVersionVerification()
.then((record) => {
console.log("\n[VERIFICATION SUMMARY JSON]");
console.log(JSON.stringify(record, null, 2));
process.exit(0);
})
.catch((error) => {
console.error("\n[REAL E2E VERIFICATION FAILED]:", error);
process.exit(1);
});
}