#!/usr/bin/env bun import { mkdtemp, readdir, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; const root = join(import.meta.dir, ".."); const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm"; async function runCommand( cmd: string[], options: { cwd: string; env?: Record; captureStdout?: boolean; timeoutMs?: number; }, ): Promise { const captureStdout = options.captureStdout === true; let timedOut = false; const proc = Bun.spawn(cmd, { cwd: options.cwd, env: { ...process.env, ...options.env, }, stdout: captureStdout ? "pipe" : "inherit", stderr: "inherit", }); const stdoutPromise = captureStdout && proc.stdout ? new Response(proc.stdout).text() : Promise.resolve(""); const timeout = setTimeout( () => { timedOut = true; proc.kill(); }, options.timeoutMs ?? 5 * 60_000, ); timeout.unref(); const exitCode = await proc.exited.finally(() => clearTimeout(timeout)); const stdout = await stdoutPromise; if (exitCode !== 0) { if (timedOut) { throw new Error( `${cmd.join(" ")} timed out after ${options.timeoutMs ?? 5 * 60_000}ms`, ); } throw new Error(`${cmd.join(" ")} exited with code ${exitCode}`); } return stdout.trim(); } async function packWorkspace( workspace: "core" | "agents" | "llms" | "shared", packDir: string, ): Promise { const destination = await mkdtemp(join(packDir, `${workspace}-`)); await runCommand(["bun", "pm", "pack", "--destination", destination], { cwd: join(root, `packages/${workspace}`), timeoutMs: 2 * 60_000, }); const files = (await readdir(destination)).filter((file) => file.endsWith(".tgz"), ); if (files.length !== 1) { throw new Error( `Expected one tarball for ${workspace}, found ${files.length}`, ); } return join(destination, files[0]); } async function main(): Promise { const packDir = await mkdtemp(join(tmpdir(), "cline-node-smoke-packs-")); const smokeDir = await mkdtemp(join(tmpdir(), "cline-node-smoke-")); const sessionsDir = await mkdtemp(join(tmpdir(), "cline-node-sessions-")); const npmCacheDir = await mkdtemp(join(tmpdir(), "cline-node-npm-cache-")); const npmEnv = { npm_config_cache: npmCacheDir }; try { console.log("Packing smoke-test tarballs with Bun..."); const tarballs = { core: await packWorkspace("core", packDir), agents: await packWorkspace("agents", packDir), llms: await packWorkspace("llms", packDir), shared: await packWorkspace("shared", packDir), }; await writeFile( join(smokeDir, "package.json"), `${JSON.stringify( { name: "cline-node-smoke", private: true, type: "module", dependencies: { "@cline/core": `file:${tarballs.core}`, "@cline/agents": `file:${tarballs.agents}`, "@cline/llms": `file:${tarballs.llms}`, "@cline/shared": `file:${tarballs.shared}`, }, }, null, 2, )}\n`, ); console.log("Installing smoke-test dependencies..."); await runCommand([npmCommand, "install"], { cwd: smokeDir, env: npmEnv, timeoutMs: 10 * 60_000, }); const nodeMajor = Number(process.versions.node.split(".")[0] || "0"); const smokeSource = nodeMajor >= 24 ? ` const { SqliteSessionStore } = await import("@cline/core"); const store = new SqliteSessionStore({ sessionsDir: process.env.CLINE_DATA_DIR }); try { store.init(); console.log("SQLite smoke test passed"); } finally { store.close(); } ` : ` const { resolveSessionBackend } = await import("@cline/core"); await resolveSessionBackend({ backendMode: "local" }); console.log("Node compatibility smoke test passed"); `; const smokeFile = join(smokeDir, "smoke.mjs"); await writeFile(smokeFile, `${smokeSource.trim()}\n`); console.log("Running smoke test..."); await runCommand(["node", smokeFile], { cwd: smokeDir, env: { ...npmEnv, CLINE_DATA_DIR: sessionsDir, }, timeoutMs: 2 * 60_000, }); } finally { await rm(packDir, { recursive: true, force: true }); await rm(smokeDir, { recursive: true, force: true }); await rm(sessionsDir, { recursive: true, force: true }); await rm(npmCacheDir, { recursive: true, force: true }); } } await main();