#!/usr/bin/env node import { spawn } from "node:child_process"; import { access, mkdtemp, rm } from "node:fs/promises"; import { constants, tmpdir } from "node:os"; import { join } from "node:path"; import { fileURLToPath } from "node:url"; import { packageRunnerCommand } from "./lib/packageRunnerCommand.mjs"; // The manifest owns this exact package pin; args[1] remains release-managed. // No build, dependency installation or ambient latest-version lookup is needed // to run this bootstrap from a Git-installed plugin. const [packageSpecifier, ...extra] = process.argv.slice(2); if ( extra.length !== 0 || !/^rea-agents@\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/u.test( packageSpecifier ?? "", ) ) { process.stderr.write( "REA plugin requires one exact rea-agents@VERSION pin.\n", ); process.exitCode = 1; } else { await launch(packageSpecifier); } async function launch(packageSpecifier) { // npm installs already carry the release-pinned runtime. Run it in this // process so the plugin keeps the caller's working directory and the // runtime retains ownership of provider and worker cleanup. const { default: bundledPackage } = await import("../package.json", { with: { type: "json" }, }); let bundledRuntimeMatchesPin = packageSpecifier === `${bundledPackage.name}@${bundledPackage.version}`; if (bundledRuntimeMatchesPin) { try { await access(new URL("../dist/mcp/main.js", import.meta.url)); } catch (cause) { if (!isMissing(cause)) throw cause; bundledRuntimeMatchesPin = false; } } if (bundledRuntimeMatchesPin) { process.argv = [ process.execPath, fileURLToPath(new URL("./rea.mjs", import.meta.url)), "mcp", ]; await import("./rea.mjs"); return; } const [command, ...args] = packageRunnerCommand( "npx", packageSpecifier, process.platform, ); // npm can satisfy the exact pin from a same-version local checkout. An // empty owned cwd prevents a Git-installed plugin or caller project from // shadowing the registry package with unbuilt/different local files. let workingDirectory; try { workingDirectory = await mkdtemp(join(tmpdir(), "rea-plugin-launcher-")); } catch (cause) { process.stderr.write( `REA plugin could not create its working directory: ${cause.message}\n`, ); process.exitCode = 1; return; } const child = spawn(command, args, { cwd: workingDirectory, stdio: "inherit", shell: false, }); let spawnFailed = false; const signals = new Map(); for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) { const forward = () => { if (child.exitCode === null && child.signalCode === null) child.kill(signal); }; signals.set(signal, forward); process.on(signal, forward); } const stopForwarding = () => { for (const [signal, listener] of signals) process.off(signal, listener); }; child.once("error", (cause) => { spawnFailed = true; stopForwarding(); process.stderr.write( `REA plugin could not start ${command}: ${cause.message}\n`, ); process.exitCode = 1; }); child.once("close", async (code, signal) => { stopForwarding(); let cleanupFailed = false; try { await rm(workingDirectory, { recursive: true }); } catch (cause) { cleanupFailed = true; process.stderr.write( `REA plugin retained its working directory at ${workingDirectory}: ${cause.message}\n`, ); } if (spawnFailed) return; if (signal !== null && process.platform !== "win32") { // Preserve signal termination after the owned child has closed its streams. process.kill(process.pid, signal); } else { process.exitCode = code === 0 && cleanupFailed ? 1 : (code ?? 128 + (constants.signals[signal] ?? 1)); } }); } function isMissing(cause) { return ( typeof cause === "object" && cause !== null && "code" in cause && cause.code === "ENOENT" ); }