diff --git a/cli/src/__tests__/company-import-export-e2e.test.ts b/cli/src/__tests__/company-import-export-e2e.test.ts index 5d49d4a8b84..5182afd3cdc 100644 --- a/cli/src/__tests__/company-import-export-e2e.test.ts +++ b/cli/src/__tests__/company-import-export-e2e.test.ts @@ -249,6 +249,16 @@ async function runCliJson( return JSON.parse(stdout.slice(jsonStart)) as T; } +// `paperclipai run` loads the CLI and the whole server graph from source +// through tsx. The test runner gives every run a fresh TMPDIR, so tsx's +// transform cache is always cold: startup then costs about twice a warm start +// (11.5 s against 5.2 s from "Starting Paperclip server..." to the first server +// log on an idle 16-core machine). On contended 4-vCPU CI runners the hooks of +// passing runs took 15-25 s, and the old 30 s budget failed in the tail. This budget +// matches the 90 s test-body timeout; a child that exits early still fails at +// once. +const SERVER_STARTUP_TIMEOUT_MS = 90_000; + async function waitForServer( instanceId: string, runtimeInfoPath: string, @@ -256,7 +266,7 @@ async function waitForServer( output: { stdout: string[]; stderr: string[] }, ) { const startedAt = Date.now(); - while (Date.now() - startedAt < 30_000) { + while (Date.now() - startedAt < SERVER_STARTUP_TIMEOUT_MS) { if (child.exitCode !== null) { throw new Error( `paperclipai run exited before healthcheck succeeded.\nstdout:\n${output.stdout.join("")}\nstderr:\n${output.stderr.join("")}`, @@ -342,7 +352,8 @@ describeEmbeddedPostgres("paperclipai company import/export e2e", () => { output, ); expect(new URL(apiBase).port).not.toBe(String(port)); - }, 60_000); + // Room for the embedded database start on top of the server budget. + }, SERVER_STARTUP_TIMEOUT_MS + 30_000); afterAll(async () => { await stopServerProcess(serverProcess); diff --git a/cli/src/__tests__/company-import-transfer.test.ts b/cli/src/__tests__/company-import-transfer.test.ts index 37c0b150bcb..0523169bc9f 100644 --- a/cli/src/__tests__/company-import-transfer.test.ts +++ b/cli/src/__tests__/company-import-transfer.test.ts @@ -1,5 +1,5 @@ import { createHash } from "node:crypto"; -import { deflateRawSync } from "node:zlib"; +import { crc32, deflateRawSync } from "node:zlib"; import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; @@ -73,12 +73,7 @@ function buildDeflateZip(entryPath: string, text: string): Uint8Array { const raw = Buffer.from(text, "utf8"); const body = deflateRawSync(raw); const name = Buffer.from(entryPath, "utf8"); - let crc = 0xffffffff; - for (const byte of raw) { - crc ^= byte; - for (let bit = 0; bit < 8; bit += 1) crc = (crc & 1) === 1 ? (crc >>> 1) ^ 0xedb88320 : crc >>> 1; - } - crc = (crc ^ 0xffffffff) >>> 0; + const crc = crc32(raw); const local = Buffer.alloc(30 + name.length); local.writeUInt32LE(0x04034b50, 0); local.writeUInt16LE(20, 4); diff --git a/cli/src/commands/client/zip.ts b/cli/src/commands/client/zip.ts index a927d239c18..c2c7a4cd06a 100644 --- a/cli/src/commands/client/zip.ts +++ b/cli/src/commands/client/zip.ts @@ -1,3 +1,5 @@ +import { crc32 } from "node:zlib"; + // The node-side portability zip reader lives in @paperclipai/shared so the // server can consume the same codec (a raw uploaded zip is unzipped into the // exact `{ rootPath, files }` bundle the inline import source carries). This @@ -31,17 +33,6 @@ function writeUint32(target: Uint8Array, offset: number, value: number) { target[offset + 3] = (value >>> 24) & 0xff; } -function crc32(bytes: Uint8Array) { - let crc = 0xffffffff; - for (const byte of bytes) { - crc ^= byte; - for (let bit = 0; bit < 8; bit += 1) { - crc = (crc & 1) === 1 ? (crc >>> 1) ^ 0xedb88320 : crc >>> 1; - } - } - return (crc ^ 0xffffffff) >>> 0; -} - /** * Build a stored (uncompressed) zip of `files` under a single `rootPath/` * top-level directory — the layout `readZipArchive` folds back into the