/** * 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); }); }