Files
PaperClipAI/tests/runner-e2e/exe-cleanup.ts
DottaandPaperclip 225dd396c5 Clean interrupted exe.dev fixtures and smoke every bundled CLI
Verify ownership before launcher cleanup when browser teardown is interrupted.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-17 10:00:37 -05:00

22 lines
1.4 KiB
TypeScript

import { createHash } from "node:crypto";
import { ssh, quote } from "../../packages/plugins/sandbox-providers/exe-dev/src/transport.js";
/** Destructive fixture cleanup requires the exact company/environment ownership tag. */
export async function cleanupExeFixture(input: {
vmName: unknown; companyId: string; environmentId: string; sshPrivateKey?: string;
}) {
if (typeof input.vmName !== "string" || !/^paperclip-e2e-[a-f0-9]{20}$/.test(input.vmName)) {
throw new Error("Refusing cleanup of a non-campaign VM");
}
if (!input.sshPrivateKey) throw new Error("Missing exe.dev cleanup credential");
const config = { sshPrivateKey: input.sshPrivateKey, strictHostKeyChecking: "accept-new" as const, timeoutMs: 120000 };
const listing = JSON.parse(await ssh(config, "exe.dev", "ls -l --json"));
const vm = listing.vms.find((entry: Record<string, unknown>) => entry.vm_name === input.vmName);
if (!vm) return;
const tag = "paperclip-" + createHash("sha256").update(`${input.companyId}\0${input.environmentId}`).digest("hex").slice(0, 32);
if (!vm.tags?.includes(tag)) throw new Error("Refusing deletion: VM ownership tag does not match this fixture");
await ssh(config, "exe.dev", `rm --json ${quote(input.vmName)}`);
const remaining = JSON.parse(await ssh(config, "exe.dev", "ls --json"));
if (remaining.vms.some((entry: Record<string, unknown>) => entry.vm_name === input.vmName)) throw new Error("exe.dev VM cleanup could not be confirmed");
}