import { json, run, runWithStatus } from "./lib/verify-package-core.mjs"; const REQUIRED_HELP_COMMANDS = [ "setup", "update", "inspect-artifact", "extract-artifact", "import-reference-source", "list-browser-targets", "inspect-web-page", "analyze-javascript-application", "compare", ]; const REQUIRED_LLM_TOPICS = [ "decompile", "function", "search", "inspect", "xrefs", "trace", "capabilities", "providers", "inspect-artifact", "list-browser-targets", "inspect-web-page", "analyze-javascript-application", ]; /** * Derive agent setup support from the doctor node check and automated Hopper * installer applicability from the facts their owners parse. * * Host readiness and installer applicability are different facts: a * distribution REA supports can still be absent from the Hopper package's * tested list, so the doctor host check cannot answer the second question. * Applicability stays undefined when its owning fact is unavailable, which is * distinct from a host known to lack an automated installer. */ export function packageSetupSupport(checks, host) { const supportedSetupHost = checks?.find(({ name }) => name === "node")?.ok === true; const hopperReadinessRequired = host.platform !== "win32" && checks?.find(({ name }) => name === "host")?.ok === true; return { supportedSetupHost, hopperReadinessRequired, hopperSetupSupported: hopperInstallerSupported( host, checks?.find(({ name }) => name === "host")?.ok, ), }; } /** * Whether `rea setup --install-hopper` can install Hopper automatically here. * On macOS the doctor host check already is the owner's installer predicate * (macOS 12 or later); on Linux it reports REA host support instead, so the * distribution's own parsed applicability governs. */ function hopperInstallerSupported(host, hostCheckOk) { if (host.platform === "win32") return false; if (host.platform === "linux") return host.linuxDistribution?.hopperSetupSupported; return hostCheckOk; } /** * The distribution facts the Hopper installer owner parses, taken from the * built application so the verifier follows the predicate the package ships. * Loaded only where a distribution can exist, which also keeps the derivation * above testable without a build. */ async function readHostLinuxDistribution() { if (process.platform !== "linux") return undefined; const { readLinuxDistribution } = await import("../dist/application/LinuxHopper.js"); return readLinuxDistribution(); } /** Validate --help, --llms, and doctor output and determine host setup support. */ export async function verifyPackageDiscovery({ cli, environment }) { const help = await run(cli, ["--help"], environment); const llms = await run(cli, ["--llms"], environment); const doctorExecution = await runWithStatus( cli, ["doctor", "--json"], environment, ); const doctor = json(doctorExecution.stdout); const { supportedSetupHost, hopperReadinessRequired, hopperSetupSupported } = packageSetupSupport(doctor.checks, { platform: process.platform, linuxDistribution: await readHostLinuxDistribution(), }); const hopperReady = doctor.checks?.find(({ name }) => name === "hopper")?.ok; const expectedDoctorHealth = doctor.checks?.every(({ ok }) => ok) === true; const missingHelp = REQUIRED_HELP_COMMANDS.filter( (command) => !help.includes(command), ); const missingLlms = REQUIRED_LLM_TOPICS.filter( (topic) => !llms.includes(topic), ); if ( missingHelp.length !== 0 || missingLlms.length !== 0 || doctor.healthy !== expectedDoctorHealth || doctorExecution.status !== (expectedDoctorHealth ? 0 : 1) || (hopperReadinessRequired && hopperReady !== true) ) throw new Error( `packaged CLI discovery or doctor failed: ${JSON.stringify({ helpSetup: help.includes("setup"), llmsDecompile: llms.includes("decompile"), doctor })}`, ); return { supportedSetupHost, hopperSetupSupported }; }