← Files ModRetro Chromatic PluginARCHIVED FILE

scripts/start-mcp.mjs

27.9 KB · Oct 2, 2026 · 00:37 UTC

↓ Download file

#!/usr/bin/env node

/**
 * Start this plugin's MCP server even when Codex cached this launcher.
 *
 * Codex plugin caches need not contain gitignored build output, npm dependencies,
 * or a multi-gigabyte local Game Boy toolchain. Keep this script dependency-free
 * and use only its own prepared runtime, an intentionally linked runtime, or
 * an explicitly configured checkout before starting the actual MCP server.
 */

import { spawn } from "node:child_process";
import { createHash } from "node:crypto";
import {
  closeSync, constants, existsSync, fstatSync, lstatSync, openSync, readSync,
  readdirSync, realpathSync, statSync, readFileSync,
} from "node:fs";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { chromaticTargetFromManifest, chromaticFormatFromManifest, requireChromaticHostTarget } from "./chromatic-runtime.mjs";

const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const localPayloadReceiptPath = ".codex-plugin/local-payload.json";
const localPayloadName = "modretro-chromatic";
const localPayloadLimits = {
  receiptBytes: 1024 * 1024,
  files: 4096,
  // Match prepare-plugin-payload.mjs and setup-runtime.mjs copy limits.
  fileBytes: 24 * 1024 * 1024,
  totalBytes: 136 * 1024 * 1024,
};
const packagedSetupGuidance = "From a trusted compiled package, use scripts/setup.sh (macOS/Linux) or scripts/setup.ps1 (Windows) to run doctor, then plan with a new owned --root. Only explicitly authorized apply may prepare a replacement; plugin installation and activation remain separate.";
const localPayloadRepair = `ModRetro Chromatic prepared runtime changed. Do not edit the immutable payload. ${packagedSetupGuidance}`;
const unsafeLauncherVariables = new Set([
  "BASH_ENV",
  "ENV",
  "NODE_OPTIONS",
  "NODE_PATH",
  "PYTHONHOME",
  "PYTHONPATH",
  "PYTHONSTARTUP",
  "PYTHONUSERBASE",
  "PYTHONWARNINGS",
  "UV_CONFIG_FILE",
  "UV_ENV_FILE",
  "UV_PROJECT",
  "UV_PROJECT_ENVIRONMENT",
  "UV_PYTHON",
  "UV_PYTHON_INSTALL_DIR",
  "UV_PYTHON_PREFERENCE",
  "UV_WORKING_DIR",
  "VIRTUAL_ENV",
]);

function readEnvironment(environment, name, platform) {
  if (platform !== "win32") return environment[name];
  const target = name.toUpperCase();
  let value;
  let found = false;

  for (const [key, candidate] of Object.entries(environment)) {
    if (key.toUpperCase() !== target) continue;
    if (found && candidate !== value) {
      throw new Error(`Conflicting case-insensitive Windows environment values exist for ${name}.`);
    }
    found = true;
    value = candidate;
  }
  return value;
}

function deleteEnvironmentAliases(environment, name, platform) {
  if (platform !== "win32") {
    delete environment[name];
    return;
  }
  const normalized = name.toUpperCase();
  for (const key of Object.keys(environment)) {
    if (key.toUpperCase() === normalized) delete environment[key];
  }
}

/** Select only signals that are actually supported by the current platform. */
export function mcpLauncherSignals(platform = process.platform) {
  return platform === "win32"
    ? ["SIGINT", "SIGTERM"]
    : ["SIGINT", "SIGTERM", "SIGHUP"];
}

/** Preserve enterprise registry/CA settings without inheriting executable hooks. */
export function prepareMcpLauncherEnvironment({
  environment = process.env,
  platform = process.platform,
  runtimeRoot,
  pluginRoot = packageRoot,
  defaultToolchainRoot,
  workingDirectory = process.cwd(),
}) {
  const childEnvironment = {};
  const seen = new Map();

  for (const [key, value] of Object.entries(environment)) {
    const normalized = platform === "win32" ? key.toUpperCase() : key;
    if (unsafeLauncherVariables.has(normalized)) continue;

    if (platform === "win32") {
      const previous = seen.get(normalized);
      if (previous !== undefined) {
        if (previous.value !== value) {
          throw new Error(`Conflicting case-insensitive Windows environment values exist for ${key}.`);
        }
        continue;
      }
      seen.set(normalized, { value });
    }
    childEnvironment[key] = value;
  }

  for (const name of ["GB_STUDIO_RUNTIME_ROOT", "GB_STUDIO_TOOLCHAIN_ROOT", "GB_STUDIO_PLUGIN_ROOT"]) {
    deleteEnvironmentAliases(childEnvironment, name, platform);
  }
  childEnvironment.GB_STUDIO_RUNTIME_ROOT = runtimeRoot;
  childEnvironment.GB_STUDIO_PLUGIN_ROOT = pluginRoot;

  const configuredToolchain = readEnvironment(environment, "GB_STUDIO_TOOLCHAIN_ROOT", platform)?.trim();
  childEnvironment.GB_STUDIO_TOOLCHAIN_ROOT = configuredToolchain
    ? path.resolve(workingDirectory, configuredToolchain)
    : defaultToolchainRoot;

  // The host replaces cwd before launching cached plugins. Only explicitly
  // configured roots can authorize a workspace; the runtime checkout never can.
  for (const variable of ["GB_STUDIO_WORKSPACE_ROOT", "GB_STUDIO_PROJECT_ROOT"]) {
    const configuredRoot = readEnvironment(environment, variable, platform)?.trim();
    deleteEnvironmentAliases(childEnvironment, variable, platform);
    if (configuredRoot) {
      childEnvironment[variable] = path.resolve(workingDirectory, configuredRoot);
    }
  }

  return childEnvironment;
}

function resolvePreparedRuntime(candidate) {
  const candidateServerPath = path.join(candidate, "dist", "server.js");

  try {
    if (!statSync(candidateServerPath).isFile()) {
      return undefined;
    }

    // A cached plugin can intentionally link dist/ to a prepared checkout.
    // Node resolves imports from the real server location, so validate the SDK
    // beside that exact runtime instead of borrowing an unrelated installation.
    const serverPath = realpathSync(candidateServerPath);
    const runtimeRoot = realpathSync(path.resolve(path.dirname(serverPath), ".."));
    const manifestPath = path.join(runtimeRoot, "package.json");
    if (existsSync(manifestPath) && JSON.parse(readFileSync(manifestPath, "utf8")).bundledMcp === true) {
      return { runtimeRoot, serverPath };
    }
    const sdkPath = path.join(runtimeRoot, "node_modules", "@modelcontextprotocol", "sdk");
    if (!statSync(sdkPath).isDirectory()) {
      return undefined;
    }
    const resolvedSdk = realpathSync(sdkPath);
    const relativeSdk = path.relative(runtimeRoot, resolvedSdk);
    if (
      relativeSdk === ".." ||
      relativeSdk.startsWith(`..${path.sep}`) ||
      path.isAbsolute(relativeSdk)
    ) {
      return undefined;
    }

    return { runtimeRoot, serverPath };
  } catch {
    return undefined;
  }
}

function canonicalPathKey(candidate) {
  const resolved = path.resolve(candidate);
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
}

function payloadDigest(contents) {
  return createHash("sha256").update(contents).digest("hex");
}

function isPayloadObject(value) {
  return value !== null && typeof value === "object" && !Array.isArray(value);
}

function safePayloadPath(relative) {
  if (
    typeof relative !== "string" || !relative || relative.length > 1024 ||
    /[\\\\:<>"|?*\u0000-\u001f]/u.test(relative) ||
    path.posix.isAbsolute(relative) || path.win32.isAbsolute(relative)
  ) throw new Error("The local payload contains an unsafe relative path.");
  const components = relative.split("/");
  if (components.length > 32 || components.some((part) =>
    !part || part === "." || part === ".." || /[. ]$/u.test(part) ||
    /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/iu.test(part)
  )) throw new Error("The local payload contains an unsafe relative path.");
  return components;
}

/** Reject links and junctions inside an already canonical, explicitly selected root. */
function unlinkedPayloadMember(root, relative) {
  const components = safePayloadPath(relative);
  let selected = root;
  let metadata;
  for (let index = 0; index < components.length; index++) {
    selected = path.join(selected, components[index]);
    metadata = lstatSync(selected);
    if (
      metadata.isSymbolicLink() ||
      canonicalPathKey(realpathSync(selected)) !== canonicalPathKey(selected)
    ) throw new Error(`The local payload member traverses a link or junction: ${relative}`);
    if (index < components.length - 1 && !metadata.isDirectory()) {
      throw new Error(`The local payload member has a non-directory parent: ${relative}`);
    }
  }
  return { selected, metadata };
}

function samePayloadFile(left, right) {
  return left.dev === right.dev && left.ino === right.ino && left.size === right.size &&
    left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs;
}

/** Hash a bounded regular file without following a replaced leaf or allocating its full size. */
function inspectPayloadFile(root, relative, { limit = localPayloadLimits.fileBytes, retainBytes = false } = {}) {
  const { selected, metadata: before } = unlinkedPayloadMember(root, relative);
  if (!before.isFile() || before.size > limit) {
    throw new Error(`The local payload member is not a bounded regular file: ${relative}`);
  }
  const descriptor = openSync(selected, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
  try {
    const opened = fstatSync(descriptor);
    if (!opened.isFile() || !samePayloadFile(before, opened)) {
      throw new Error(`The local payload member changed while opening it: ${relative}`);
    }
    const hash = createHash("sha256");
    const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, Math.max(1, before.size + 1)));
    const chunks = [];
    let size = 0;
    for (;;) {
      const count = readSync(descriptor, buffer, 0, Math.min(buffer.length, limit - size + 1), null);
      if (count === 0) break;
      size += count;
      if (size > limit || size > before.size) {
        throw new Error(`The local payload member grew while reading it: ${relative}`);
      }
      const chunk = buffer.subarray(0, count);
      hash.update(chunk);
      if (retainBytes) chunks.push(Buffer.from(chunk));
    }
    const after = fstatSync(descriptor);
    const current = unlinkedPayloadMember(root, relative).metadata;
    if (size !== before.size || !samePayloadFile(opened, after) || !samePayloadFile(after, current)) {
      throw new Error(`The local payload member changed while reading it: ${relative}`);
    }
    return {
      size,
      mode: before.mode & 0o111 ? 0o755 : 0o644,
      sha256: hash.digest("hex"),
      ...(retainBytes ? { contents: Buffer.concat(chunks, size) } : {}),
    };
  } finally {
    closeSync(descriptor);
  }
}

function payloadJson(contents, label) {
  let parsed;
  try { parsed = JSON.parse(contents.toString("utf8")); }
  catch { throw new Error(`The ${label} is not valid JSON.`); }
  if (!isPayloadObject(parsed)) throw new Error(`The ${label} must be a JSON object.`);
  return parsed;
}

function payloadInventory(value, label) {
  if (!Array.isArray(value) || value.length === 0 || value.length > localPayloadLimits.files) {
    throw new Error(`The ${label} is missing or oversized.`);
  }
  const seen = new Set();
  let totalBytes = 0;
  let previousPath;
  return value.map((entry) => {
    if (!isPayloadObject(entry) || Object.keys(entry).length !== 4 ||
      !["path", "mode", "size", "sha256"].every((key) => Object.hasOwn(entry, key))) {
      throw new Error(`The ${label} contains a malformed file record.`);
    }
    safePayloadPath(entry.path);
    const key = process.platform === "win32" ? entry.path.toLowerCase() : entry.path;
    if (entry.path === localPayloadReceiptPath || seen.has(key) ||
      (previousPath !== undefined && previousPath.localeCompare(entry.path, "en") >= 0) ||
      ![0o644, 0o755].includes(entry.mode) || !Number.isSafeInteger(entry.size) ||
      entry.size < 0 || entry.size > localPayloadLimits.fileBytes ||
      typeof entry.sha256 !== "string" || !/^[a-f0-9]{64}$/u.test(entry.sha256)) {
      throw new Error(`The ${label} contains an invalid or duplicate file record.`);
    }
    totalBytes += entry.size;
    if (totalBytes > localPayloadLimits.totalBytes) throw new Error(`The ${label} exceeds its byte limit.`);
    previousPath = entry.path;
    seen.add(key);
    return { path: entry.path, mode: entry.mode, size: entry.size, sha256: entry.sha256 };
  });
}

function isRuntimePayloadFile(relative) {
  return relative.startsWith("dist/") || relative.startsWith("assets/devices/") || relative === "scripts/emulator_worker.py" ||
    ["scripts/dependency-doctor.mjs", "scripts/dependency-catalog.mjs", "scripts/node-releases.tsv", "scripts/setup-io.mjs", "scripts/chromatic.mjs", "scripts/chromatic-runtime.mjs"].includes(relative) ||
    relative.startsWith("third-party/chromatic-cli/") ||
    (relative.startsWith("native/capture/macos/") || relative.startsWith("native/capture/linux/")) || relative.startsWith("native-game-plugin/") || relative.startsWith("examples/");
}

/** An immutable cache has no runtime dependencies, toolchain, or unrecorded files. */
function assertExactPayloadTree(root, records, additionalFiles = []) {
  const expected = new Set([...records.map((entry) => entry.path), ...additionalFiles]);
  const directories = new Set([""]);
  for (const relative of expected) {
    const components = safePayloadPath(relative);
    for (let index = 1; index < components.length; index++) {
      directories.add(components.slice(0, index).join("/"));
    }
  }
  const found = new Set();
  function visit(relativeDirectory) {
    const directory = relativeDirectory
      ? unlinkedPayloadMember(root, relativeDirectory)
      : { selected: root, metadata: lstatSync(root) };
    if (!directory.metadata.isDirectory()) throw new Error("The local payload contains a non-directory parent.");
    for (const entry of readdirSync(directory.selected, { withFileTypes: true })) {
      const relative = relativeDirectory ? `${relativeDirectory}/${entry.name}` : entry.name;
      safePayloadPath(relative);
      if (entry.isSymbolicLink()) throw new Error(`The local payload contains a link: ${relative}`);
      if (entry.isDirectory()) {
        if (!directories.has(relative)) throw new Error(`The local payload contains an unexpected directory: ${relative}`);
        visit(relative);
      } else {
        if (!entry.isFile() || !expected.has(relative) || found.has(relative)) {
          throw new Error(`The local payload contains an unexpected file: ${relative}`);
        }
        found.add(relative);
      }
    }
  }
  visit("");
  if (found.size !== expected.size) throw new Error("The local payload is incomplete.");
}

function matchingPayloadFile(root, record, { retainBytes = false, checkMode = true } = {}) {
  const actual = inspectPayloadFile(root, record.path, { retainBytes });
  if ((checkMode && actual.mode !== record.mode) || actual.size !== record.size || actual.sha256 !== record.sha256) {
    throw new Error(`The recorded file no longer matches: ${record.path}`);
  }
  return actual.contents;
}

function hasPreparedPayloadManifest(pluginRoot) {
  try {
    const manifest = payloadJson(inspectPayloadFile(realpathSync(pluginRoot), ".codex-plugin/plugin.json", {
      limit: localPayloadLimits.receiptBytes, retainBytes: true,
    }).contents, "plugin manifest");
    return manifest.name === localPayloadName && typeof manifest.version === "string" &&
      manifest.version.includes("+codex.payload.");
  } catch {
    // Receipt-free generic packages never required a manifest or its format.
    return false;
  }
}

/**
 * Verify a machine-local prepared payload before executing checkout code.
 * Ordinary packages without a receipt intentionally retain the legacy launcher.
 */
export function verifyLocalPayloadRuntime({ pluginRoot = packageRoot, runtimeRoot } = {}) {
  try {
    try { lstatSync(path.join(pluginRoot, ...localPayloadReceiptPath.split("/"))); }
    catch (error) {
      if (error.code === "ENOENT" || error.code === "ENOTDIR") {
        if (hasPreparedPayloadManifest(pluginRoot)) throw new Error("The prepared plugin's local payload receipt is missing.");
        return undefined;
      }
      throw error;
    }
    const cachedRoot = realpathSync(pluginRoot);
    if (!statSync(cachedRoot).isDirectory()) throw new Error("The cached plugin root is not a directory.");
    const receipt = payloadJson(inspectPayloadFile(cachedRoot, localPayloadReceiptPath, {
      limit: localPayloadLimits.receiptBytes, retainBytes: true,
    }).contents, "local payload receipt");
    if (receipt.schemaVersion !== 1 || receipt.pluginName !== localPayloadName ||
      typeof receipt.sourceVersion !== "string" || receipt.sourceVersion.length > 200 ||
      typeof receipt.version !== "string" || receipt.version.length > 200 ||
      typeof receipt.payloadId !== "string" || !/^[a-f0-9]{64}$/u.test(receipt.payloadId) ||
      !isPayloadObject(receipt.runtime) || receipt.runtime.packageName !== localPayloadName ||
      typeof receipt.runtime.version !== "string" || receipt.runtime.version !== receipt.sourceVersion.split("+")[0]) {
      throw new Error("The local payload receipt has an unsupported identity or schema.");
    }
    for (const key of ["runtimeRoot", "toolchainRoot", "nodeExecutable"]) {
      if (typeof receipt[key] !== "string" || !path.isAbsolute(receipt[key]) || /[\u0000-\u001f]/u.test(receipt[key])) {
        throw new Error(`The local payload receipt has an invalid ${key}.`);
      }
    }
    if (typeof runtimeRoot !== "string" ||
      canonicalPathKey(realpathSync(runtimeRoot)) !== canonicalPathKey(receipt.runtimeRoot) ||
      canonicalPathKey(realpathSync(receipt.runtimeRoot)) !== canonicalPathKey(receipt.runtimeRoot) ||
      !statSync(receipt.runtimeRoot).isDirectory()) {
      throw new Error("The selected prepared runtime does not match the recorded canonical runtime root.");
    }
    const selectedRuntime = realpathSync(runtimeRoot);
    const files = payloadInventory(receipt.files, "payload inventory");
    const runtimeFiles = payloadInventory(receipt.runtime.files, "runtime inventory");
    if (payloadDigest(JSON.stringify(files)) !== receipt.payloadId ||
      JSON.stringify(runtimeFiles) !== JSON.stringify(files.filter((entry) => isRuntimePayloadFile(entry.path)))) {
      throw new Error("The local payload receipt inventories do not match their recorded identity.");
    }
    const byPath = new Map(files.map((entry) => [entry.path, entry]));
    for (const required of [".codex-plugin/plugin.json", ".mcp.json", "package.json", "scripts/start-mcp.mjs", "scripts/chromatic-runtime.mjs", "scripts/emulator_worker.py", "dist/server.js"]) {
      if (!byPath.has(required)) throw new Error(`The local payload receipt is missing ${required}.`);
    }
    assertExactPayloadTree(cachedRoot, files, [localPayloadReceiptPath]);
    const jsonFiles = new Map();
    for (const record of files) {
      const retainBytes = [".codex-plugin/plugin.json", ".mcp.json", "package.json"].includes(record.path);
      const contents = matchingPayloadFile(cachedRoot, record, { retainBytes });
      if (retainBytes) jsonFiles.set(record.path, payloadJson(contents, record.path));
    }
    const pluginManifest = jsonFiles.get(".codex-plugin/plugin.json");
    const packageManifest = jsonFiles.get("package.json");
    const mcpConfiguration = jsonFiles.get(".mcp.json")?.mcpServers?.["modretro-chromatic"];
    if (pluginManifest.name !== localPayloadName || pluginManifest.version !== receipt.version ||
      packageManifest.name !== localPayloadName || packageManifest.version !== receipt.runtime.version) {
      throw new Error("The cached plugin or package version does not match its receipt.");
    }
    const sourceManifest = Buffer.from(`${JSON.stringify({ ...pluginManifest, version: receipt.sourceVersion }, null, 2)}\n`);
    const originalFiles = files.map((entry) => entry.path === ".codex-plugin/plugin.json"
      ? { ...entry, size: sourceManifest.length, sha256: payloadDigest(sourceManifest) }
      : entry);
    const expectedVersion = `${receipt.runtime.version}+codex.payload.${payloadDigest(JSON.stringify(originalFiles)).slice(0, 24)}`;
    if (receipt.version !== expectedVersion) throw new Error("The cached plugin version is not bound to its prepared contents.");
    if (!isPayloadObject(mcpConfiguration) || mcpConfiguration.command !== receipt.nodeExecutable ||
      mcpConfiguration.env?.GB_STUDIO_RUNTIME_ROOT !== receipt.runtimeRoot ||
      mcpConfiguration.env?.GB_STUDIO_TOOLCHAIN_ROOT !== receipt.toolchainRoot) {
      throw new Error("The cached MCP launcher configuration does not match its receipt.");
    }
    const selectedPackage = payloadJson(inspectPayloadFile(selectedRuntime, "package.json", {
      limit: localPayloadLimits.receiptBytes, retainBytes: true,
    }).contents, "prepared runtime package manifest");
    if (selectedPackage.name !== localPayloadName || selectedPackage.version !== receipt.runtime.version) {
      throw new Error("The selected prepared runtime package name or version changed.");
    }
    const target = chromaticTargetFromManifest(packageManifest);
    const format = chromaticFormatFromManifest(packageManifest);
    if (chromaticTargetFromManifest(selectedPackage) !== target) {
      throw new Error("The cached plugin and selected runtime target different platforms.");
    }
    if (chromaticFormatFromManifest(selectedPackage) !== format) throw new Error("Runtime and package Chromatic formats differ.");
    requireChromaticHostTarget(target);
    const distFiles = runtimeFiles.filter((entry) => entry.path.startsWith("dist/"))
      .map((entry) => ({ ...entry, path: entry.path.slice("dist/".length) }));
    const runtimeDist = unlinkedPayloadMember(selectedRuntime, "dist");
    if (!runtimeDist.metadata.isDirectory()) throw new Error("The selected prepared runtime has no real dist directory.");
    assertExactPayloadTree(runtimeDist.selected, distFiles);
    const chromaticPrefix = "third-party/chromatic-cli/";
    const chromaticFiles = runtimeFiles.filter((entry) => entry.path.startsWith(chromaticPrefix));
    if (chromaticFiles.length) {
      const nativeRoot = unlinkedPayloadMember(selectedRuntime, chromaticPrefix.slice(0, -1));
      if (!nativeRoot.metadata.isDirectory()) throw new Error("The bundled Chromatic runtime is not a real directory.");
      assertExactPayloadTree(nativeRoot.selected, chromaticFiles.map((entry) => ({ ...entry, path: entry.path.slice(chromaticPrefix.length) })));
    }
    const capturePrefixes = ["native/capture/macos/", "native/capture/linux/"];
    for (const capturePrefix of capturePrefixes) {
      const captureFiles = runtimeFiles.filter((entry) => entry.path.startsWith(capturePrefix));
      if (captureFiles.length) {
        const captureRoot = unlinkedPayloadMember(selectedRuntime, capturePrefix.slice(0, -1));
        if (!captureRoot.metadata.isDirectory()) throw new Error("The native capture runtime is not a real directory.");
        assertExactPayloadTree(captureRoot.selected, captureFiles.map((entry) => ({ ...entry, path: entry.path.slice(capturePrefix.length) })));
      }
    }
    const samplePrefix = "examples/wrecklight/";
    const sampleFiles = runtimeFiles.filter((entry) => entry.path.startsWith(samplePrefix));
    if (sampleFiles.length) {
      const sampleRoot = unlinkedPayloadMember(selectedRuntime, samplePrefix.slice(0, -1));
      if (!sampleRoot.metadata.isDirectory()) throw new Error("The bundled Wrecklight sample is not a real directory.");
      assertExactPayloadTree(sampleRoot.selected, sampleFiles.map((entry) => ({ ...entry, path: entry.path.slice(samplePrefix.length) })));
    }
    for (const record of runtimeFiles) matchingPayloadFile(selectedRuntime, record, { checkMode: capturePrefixes.some(prefix => record.path.startsWith(prefix)) || record.path.startsWith(chromaticPrefix) || record.path.startsWith(samplePrefix) });
    return receipt;
  } catch (error) {
    throw new Error(`${localPayloadRepair} ${error.message}`, { cause: error });
  }
}

function verifyLauncherPlatform(root) {
  const selected = realpathSync(root);
  try { lstatSync(path.join(selected, "package.json")); }
  catch (error) {
    // Preserve legacy receipt-free launchers that have no package manifest.
    if (error.code === "ENOENT") return;
    throw error;
  }
  const manifest = payloadJson(inspectPayloadFile(selected, "package.json", {
    limit: localPayloadLimits.receiptBytes, retainBytes: true,
  }).contents, "launcher package manifest");
  requireChromaticHostTarget(chromaticTargetFromManifest(manifest));
  chromaticFormatFromManifest(manifest);
}

function startMcpLauncher() {
  verifyLauncherPlatform(packageRoot);
  const platform = process.platform;
  const requestedRuntimeRoot = readEnvironment(process.env, "GB_STUDIO_RUNTIME_ROOT", platform)?.trim();
  const candidateRoot = path.resolve(requestedRuntimeRoot || packageRoot);
  const preparedRuntime = resolvePreparedRuntime(candidateRoot);
  verifyLocalPayloadRuntime({ runtimeRoot: preparedRuntime?.runtimeRoot });

  if (!preparedRuntime) {
    const selection = requestedRuntimeRoot
      ? "the explicitly configured runtime"
      : "this installed plugin";
    console.error(
      `ModRetro Chromatic MCP could not find a prepared runtime for ${selection}. ` +
        `${packagedSetupGuidance} ` +
        `Inspected: ${candidateRoot}`,
    );
    process.exitCode = 1;
    return;
  }

  const { runtimeRoot, serverPath } = preparedRuntime;
  verifyLauncherPlatform(runtimeRoot);
  const localToolchainPath = path.join(runtimeRoot, ".local");
  const bundled = existsSync(path.join(runtimeRoot, "package.json")) && JSON.parse(readFileSync(path.join(runtimeRoot, "package.json"), "utf8")).bundledMcp === true;
  const home = readEnvironment(process.env, platform === "win32" ? "USERPROFILE" : "HOME", platform) || os.homedir();
  const dataRoot = platform === "darwin" ? path.join(home, "Library", "Application Support")
    : platform === "win32" ? readEnvironment(process.env, "LOCALAPPDATA", platform) || path.join(home, "AppData", "Local")
      : readEnvironment(process.env, "XDG_DATA_HOME", platform) || path.join(home, ".local", "share");
  const setupRoot = readEnvironment(process.env, "GB_STUDIO_SETUP_ROOT", platform) || path.join(dataRoot, "modretro-chromatic");
  if (!path.isAbsolute(setupRoot)) throw new Error("The dependency setup root must be absolute.");
  let defaultToolchainRoot = bundled ? path.join(setupRoot, "toolchain") : runtimeRoot;
  if (existsSync(localToolchainPath)) {
    const resolvedLocal = realpathSync(localToolchainPath);
    if (!statSync(resolvedLocal).isDirectory() || path.basename(resolvedLocal).toLowerCase() !== ".local") {
      throw new Error("The trusted local game toolchain must resolve to a real .local directory.");
    }
    defaultToolchainRoot = path.dirname(resolvedLocal);
  }

  const childEnvironment = prepareMcpLauncherEnvironment({
    environment: process.env,
    platform,
    runtimeRoot,
    pluginRoot: realpathSync(packageRoot),
    defaultToolchainRoot,
  });
  const child = spawn(process.execPath, [serverPath, ...process.argv.slice(2)], {
    cwd: runtimeRoot,
    env: childEnvironment,
    stdio: "inherit",
    shell: false,
    windowsHide: true,
  });

  const forwardSignal = (signal) => {
    if (!child.killed && child.exitCode === null) {
      child.kill(signal);
    }
  };
  const cleanupChild = () => forwardSignal("SIGTERM");
  const handlers = new Map();
  for (const signal of mcpLauncherSignals(platform)) {
    const handler = () => forwardSignal(signal);
    handlers.set(signal, handler);
    process.on(signal, handler);
  }
  process.once("exit", cleanupChild);

  const removeHandlers = () => {
    for (const [signal, handler] of handlers) process.removeListener(signal, handler);
    process.removeListener("exit", cleanupChild);
  };

  child.once("error", (error) => {
    removeHandlers();
    console.error(`ModRetro Chromatic MCP could not start ${serverPath}: ${error.message}`);
    process.exitCode = 1;
  });

  child.once("exit", (code, signal) => {
    removeHandlers();
    if (code !== null) {
      process.exitCode = code;
    } else {
      const signalCode = signal === null ? 0 : os.constants.signals[signal] || 0;
      process.exitCode = 128 + signalCode;
    }
  });
}

const invokedEntrypoint = process.argv[1];
if (
  invokedEntrypoint &&
  realpathSync(fileURLToPath(import.meta.url)) === realpathSync(path.resolve(invokedEntrypoint))
) {
  try {
    startMcpLauncher();
  } catch (error) {
    console.error(`ModRetro Chromatic MCP could not start safely: ${error.message}`);
    process.exitCode = 1;
  }
}

SHA-256: b6cbcd4b810e72bcc227906520264a1678902ce78d63dd647794ec8b7b27529e