#!/usr/bin/env node import { spawn } from "node:child_process"; import { mkdtemp, readdir, readFile, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join, resolve } from "node:path"; const projectRoot = resolve(import.meta.dirname, ".."); async function readPublishedBinName() { const packageJsonRaw = await readFile(join(projectRoot, "package.json"), "utf8"); const packageJson = JSON.parse(packageJsonRaw); const binNames = Object.keys(packageJson.bin ?? {}); if (binNames.length !== 1) { throw new Error(`Expected exactly one published bin entry, found ${binNames.length}`); } return binNames[0]; } function run(command, args, options = {}) { return new Promise((resolvePromise, rejectPromise) => { const child = spawn(command, args, { cwd: projectRoot, stdio: ["ignore", "pipe", "pipe"], ...options, }); let stdout = ""; let stderr = ""; child.stdout.on("data", (chunk) => { stdout += chunk.toString(); }); child.stderr.on("data", (chunk) => { stderr += chunk.toString(); }); child.on("error", rejectPromise); child.on("exit", (code) => { if (code === 0) { resolvePromise({ stdout, stderr }); return; } rejectPromise( new Error( `${command} ${args.join(" ")} failed with code ${code}\nSTDOUT:\n${stdout}\nSTDERR:\n${stderr}`, ), ); }); }); } async function verifyPackagedBin() { const packDir = await mkdtemp(join(tmpdir(), "mcp-template-pack-")); const installDir = await mkdtemp(join(tmpdir(), "mcp-template-install-")); const binName = await readPublishedBinName(); try { await run("pnpm", ["pack", "--pack-destination", packDir]); const tarballs = (await readdir(packDir)).filter((name) => name.endsWith(".tgz")); if (tarballs.length !== 1) { throw new Error(`Expected exactly one tarball, found ${tarballs.length}`); } const tarballPath = join(packDir, tarballs[0]); await writeFile(join(installDir, "package.json"), '{"name":"publish-bin-check","private":true}\n'); await run("npm", ["install", tarballPath], { cwd: installDir }); const startup = spawn(join(installDir, "node_modules", ".bin", binName), [], { cwd: installDir, stdio: ["ignore", "pipe", "pipe"], }); const startupOutput = await new Promise((resolvePromise, rejectPromise) => { let stderr = ""; const timeout = setTimeout(() => { startup.kill("SIGTERM"); rejectPromise(new Error(`Timed out waiting for packaged bin to start\nSTDERR:\n${stderr}`)); }, 10000); startup.stderr.on("data", (chunk) => { stderr += chunk.toString(); if (stderr.includes("MCP stdio server started")) { clearTimeout(timeout); startup.kill("SIGTERM"); resolvePromise(stderr); } }); startup.on("error", (error) => { clearTimeout(timeout); rejectPromise(error); }); startup.on("exit", (code, signal) => { if (signal === "SIGTERM") { return; } clearTimeout(timeout); rejectPromise( new Error( `Packaged bin exited before startup completed (code=${code}, signal=${signal})\nSTDERR:\n${stderr}`, ), ); }); }); if (!String(startupOutput).includes("MCP stdio server started")) { throw new Error("Packaged bin did not emit the expected startup log"); } } finally { await rm(packDir, { recursive: true, force: true }); await rm(installDir, { recursive: true, force: true }); } } await verifyPackagedBin();