← Files ModRetro Chromatic PluginARCHIVED FILE

scripts/setup-compiler.mjs

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

↓ Download file

#!/usr/bin/env node

/** Portable launcher for the existing Unix setup and the experimental Windows x64 setup. */

import { execFile, spawn } from "node:child_process";
import { createHash, randomUUID } from "node:crypto";
import { createReadStream, createWriteStream } from "node:fs";
import {
  cp,
  lstat,
  mkdir,
  mkdtemp,
  open,
  readFile,
  readdir,
  realpath,
  rename,
  rm,
  writeFile,
} from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { Readable, Transform } from "node:stream";
import { pipeline } from "node:stream/promises";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";

const execFileAsync = promisify(execFile);
const SCRIPT_PATH = fileURLToPath(import.meta.url);
const SCRIPT_DIRECTORY = path.dirname(SCRIPT_PATH);
const PLUGIN_ROOT = path.resolve(SCRIPT_DIRECTORY, "..");
const MAX_ARCHIVE_BYTES = 512 * 1024 * 1024;
const MAX_ARCHIVE_ENTRIES = 32_768;
const MAX_EXTRACTED_BYTES = 2 * 1024 * 1024 * 1024;
const MAX_CENTRAL_DIRECTORY_BYTES = 64 * 1024 * 1024;
const PROGRESS_INTERVAL_MS = 30_000;
const MAX_PROGRESS_UPDATES = 60;
const COREPACK_REMEDIATION = "Use a current supported Node.js 22 or 24 LTS, run `npm install --global corepack@latest`, ensure the npm global directory is on PATH, then rerun setup.";
const WINDOWS_RESERVED_NAME = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/iu;
const WINDOWS_INVALID_COMPONENT = /[<>:"|?*\u0000-\u001f]|[. ]$/u;
const UNSAFE_ENVIRONMENT_KEYS = new Set([
  "BASH_ENV",
  "COMSPEC",
  "COREPACK_HOME",
  "COREPACK_DEFAULT_TO_LATEST",
  "COREPACK_ENABLE_AUTO_PIN",
  "COREPACK_ENABLE_DOWNLOAD_PROMPT",
  "COREPACK_ENABLE_PROJECT_SPEC",
  "COREPACK_ENABLE_STRICT",
  "COREPACK_ENABLE_UNSAFE_CUSTOM_URLS",
  "COREPACK_ENV_FILE",
  "COREPACK_INTEGRITY_KEYS",
  "ENV",
  "NODE_OPTIONS",
  "NODE_PATH",
  "NODE_EXTRA_CA_CERTS",
  "NODE_REPL_HISTORY",
  "PYTHONPATH",
  "PYTHONHOME",
  "PYTHONSTARTUP",
  "PYTHONUSERBASE",
  "PYTHONWARNINGS",
  "PSMODULEPATH",
  "PSMODULEANALYSISCACHEPATH",
  "PSEXECUTIONPOLICYPREFERENCE",
  "__PSLOCKDOWNPOLICY",
  "VIRTUAL_ENV",
  "TMP",
  "TEMP",
  "TMPDIR",
  "TAR_OPTIONS",
  "TAR_READER_OPTIONS",
  "TAR_WRITER_OPTIONS",
  "YARN_NO_PROXY",
  "YARN_NPM_MINIMAL_AGE_GATE",
  "YARN_GLOBAL_FOLDER",
  "YARN_CACHE_FOLDER",
  "YARN_ENABLE_GLOBAL_CACHE",
]);

export const WINDOWS_TOOLCHAIN_RELEASES = Object.freeze({
  gbStudio: Object.freeze({
    version: "4.3.2",
    commit: "ccb891b2670134ba8237416772eea4ed09d34e1e",
    gbvmCommit: "bd6f41cc5e05cbe6601dcc7f8e2db89bed527fe3",
    archive: "gb-studio-windows-64bit-standalone.zip",
    sha256: "9afea7d8f3920991c3562fd69fa8a98aed7f29d231aa468bb4dbcf123b60db80",
  }),
  gbdk: Object.freeze({
    version: "4.5.0",
    archive: "gbdk-win64.zip",
    sha256: "266854ce92e3064871c5b28cd3436cc2a6cb136af9e7cf617140108f8c1c5890",
  }),
  platform: "win32-x64",
});

class SetupUsageError extends Error {
  exitCode = 2;
}

const SETUP_USAGE = [
  "Usage: node scripts/setup-compiler.mjs [--headless] [--with-cli] [--with-desktop] [--use-existing] [--dry-run]",
  "  macOS defaults to the verified desktop editor and GBDK.",
  "  Linux defaults to headless official project compilation and GBDK.",
  "  Experimental Windows x64 setup defaults to the official headless compiler and GBDK.",
  "  --use-existing executes version probes; --dry-run prints an unverified plan without running tools.",
  "  For installed plugins, use packaged setup.mjs (or setup.sh/setup.ps1) with an external dependency root.",
].join("\n");

function systemError(error, code) {
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
}

function samePath(left, right, platform = process.platform) {
  const resolvedLeft = path.resolve(left);
  const resolvedRight = path.resolve(right);
  return platform === "win32"
    ? resolvedLeft.toLowerCase() === resolvedRight.toLowerCase()
    : resolvedLeft === resolvedRight;
}

function withinRoot(root, candidate) {
  const relative = path.relative(root, candidate);
  return relative === "" ||
    (relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
}

async function metadataIfPresent(candidate) {
  try {
    return await lstat(candidate);
  } catch (error) {
    if (systemError(error, "ENOENT")) return undefined;
    throw error;
  }
}

/** Legacy developer installers must never add files to a prepared immutable payload. */
export async function assertLegacySetupMutableRoot(root) {
  let candidate = path.resolve(root);
  for (;;) {
    let immutable = await metadataIfPresent(path.join(candidate, ".codex-plugin", "local-payload.json")) !== undefined;
    const manifest = path.join(candidate, ".codex-plugin", "plugin.json");
    const metadata = await metadataIfPresent(manifest);
    if (!immutable && metadata?.isSymbolicLink()) {
      throw new Error("Legacy setup cannot establish a mutable root through a linked plugin manifest. Use packaged setup.mjs with an external dependency root.");
    }
    if (!immutable && metadata?.isFile() && !metadata.isSymbolicLink()) {
      const version = JSON.parse(await readFile(manifest, "utf8")).version;
      immutable = typeof version === "string" && version.includes("+codex.payload.");
    }
    if (immutable) {
      throw new Error("Legacy setup cannot write into an immutable plugin payload or cache. " +
        "Use packaged setup.mjs (or setup.sh/setup.ps1) with an external dependency root.");
    }
    const parent = path.dirname(candidate);
    if (parent === candidate) return;
    candidate = parent;
  }
}

async function requireRealDirectory(candidate, label, platform) {
  const metadata = await metadataIfPresent(candidate);
  if (!metadata || metadata.isSymbolicLink() || !metadata.isDirectory()) {
    throw new Error(`${label} must be a real existing directory: ${candidate}`);
  }
  const canonical = await realpath(candidate);
  if (!samePath(canonical, candidate, platform)) {
    throw new Error(`${label} must not resolve through a symbolic link or junction: ${candidate}`);
  }
  return canonical;
}

async function requireRealFile(candidate, label, platform) {
  const metadata = await metadataIfPresent(candidate);
  if (!metadata || metadata.isSymbolicLink() || !metadata.isFile()) {
    throw new Error(`${label} must be a real regular file: ${candidate}`);
  }
  if (!samePath(await realpath(candidate), candidate, platform)) {
    throw new Error(`${label} must not resolve through a symbolic link or junction: ${candidate}`);
  }
  return candidate;
}

async function ensureManagedDirectory(root, candidate, platform) {
  const resolved = path.resolve(candidate);
  if (!withinRoot(root, resolved)) {
    throw new Error(`A managed GB Studio path escapes its trusted toolchain: ${candidate}`);
  }
  const parts = path.relative(root, resolved).split(path.sep).filter(Boolean);
  let current = root;
  await requireRealDirectory(current, "The trusted game toolchain root", platform);
  for (const segment of parts) {
    current = path.join(current, segment);
    const metadata = await metadataIfPresent(current);
    if (!metadata) {
      await mkdir(current);
    } else if (metadata.isSymbolicLink() || !metadata.isDirectory()) {
      throw new Error(`A managed game toolchain path must be a real directory: ${current}`);
    }
    await requireRealDirectory(current, "A managed game toolchain path", platform);
  }
  return current;
}

async function assertManagedPathSafe(root, candidate, platform) {
  const resolved = path.resolve(candidate);
  if (!withinRoot(root, resolved)) {
    throw new Error(`A managed GB Studio path escapes its trusted toolchain: ${candidate}`);
  }
  let current = root;
  for (const segment of path.relative(root, resolved).split(path.sep).filter(Boolean)) {
    current = path.join(current, segment);
    const metadata = await metadataIfPresent(current);
    if (!metadata) return;
    if (metadata.isSymbolicLink()) {
      throw new Error(`A managed game toolchain path must not be a symbolic link or junction: ${current}`);
    }
    if (!samePath(await realpath(current), current, platform)) {
      throw new Error(`A managed game toolchain path resolves through a junction: ${current}`);
    }
  }
}

/** Windows environment-variable names are case-insensitive; never keep poisoned aliases. */
export function sanitizeWindowsBootstrapEnvironment(environment = process.env, temporaryDirectory) {
  const sanitized = {};
  const retained = new Map();
  for (const [name, value] of Object.entries(environment)) {
    if (value === undefined) continue;
    const normalized = name.toUpperCase();
    if (retained.has(normalized)) {
      if (retained.get(normalized) !== value) {
        throw new Error(`The Windows bootstrap environment contains conflicting case-insensitive ${normalized} values.`);
      }
      continue;
    }
    retained.set(normalized, value);
    if (
      UNSAFE_ENVIRONMENT_KEYS.has(normalized) ||
      normalized.startsWith("UV_") ||
      normalized.startsWith("GIT_")
    ) continue;
    sanitized[normalized === "PATH" ? "PATH" : name] = value;
  }
  if (temporaryDirectory !== undefined) {
    sanitized.TMP = temporaryDirectory;
    sanitized.TEMP = temporaryDirectory;
    sanitized.TMPDIR = temporaryDirectory;
  }
  return sanitized;
}

export function parseWindowsSetupArguments(arguments_) {
  const options = {
    headless: false,
    withCli: true,
    withDesktop: false,
    useExisting: false,
    dryRun: false,
    help: false,
  };
  for (const argument of arguments_) {
    switch (argument) {
      case "--headless":
        options.headless = true;
        break;
      case "--with-cli":
        options.withCli = true;
        break;
      case "--with-desktop":
        options.withDesktop = true;
        break;
      case "--use-existing":
        options.useExisting = true;
        break;
      case "--dry-run":
        options.dryRun = true;
        break;
      case "--help":
      case "-h":
        options.help = true;
        break;
      default:
        throw new SetupUsageError(`Unknown ModRetro Chromatic setup option: ${argument}`);
    }
  }
  if (options.headless && options.withDesktop) {
    throw new SetupUsageError("--headless and --with-desktop cannot be combined.");
  }
  return options;
}

function gbStudioSetupPlan(options, selected, logger) {
  const platform = options.platform ?? process.platform;
  const architecture = options.arch ?? process.arch;
  const environment = options.environment ?? process.env;
  const configuredRoot = platform === "win32"
    ? caseInsensitiveValue(environment, "GB_STUDIO_TOOLCHAIN_ROOT")
    : environment.GB_STUDIO_TOOLCHAIN_ROOT;
  const toolchainRoot = path.resolve(options.workingDirectory ?? options.cwd ?? process.cwd(), options.toolchainRoot ??
    configuredRoot ?? options.pluginRoot ?? PLUGIN_ROOT);
  const withCli = platform === "win32" || platform === "linux" || selected.headless || selected.useExisting || (options.args ?? []).includes("--with-cli");
  const withDesktop = selected.withDesktop || (platform === "darwin" && !selected.headless && !selected.useExisting);
  const components = [`GBDK ${WINDOWS_TOOLCHAIN_RELEASES.gbdk.version}`,
    ...(withCli ? [`official GB Studio ${WINDOWS_TOOLCHAIN_RELEASES.gbStudio.version} CLI`] : []),
    ...(withDesktop ? [`optional GB Studio ${WINDOWS_TOOLCHAIN_RELEASES.gbStudio.version} desktop editor`] : [])];
  logger("[dry-run] Plan only; no tools were run and dependency health is unverified.");
  logger(`[dry-run] Target ${platform}-${architecture}; dependency destination ${path.join(toolchainRoot, ".local")}.`);
  logger(`[dry-run] Would ${selected.useExisting ? "verify the existing" : "verify/reuse or prepare"} ${components.join(", ")}.`);
  logger("[dry-run] Installed plugins should use packaged setup.mjs (or setup.sh/setup.ps1) with an external dependency root.");
  return { schemaVersion: 1, dryRun: true, verified: false, installed: false, platform: `${platform}-${architecture}`,
    toolchainRoot, components, useExisting: selected.useExisting };
}

function validateArchiveName(name, seen) {
  if (
    !name || name.includes("\u0000") || name.startsWith("/") || name.startsWith("\\") ||
    /^[A-Za-z]:/u.test(name)
  ) {
    throw new Error(`The verified release archive contains an unsafe absolute entry: ${JSON.stringify(name)}`);
  }
  const directory = name.endsWith("/") || name.endsWith("\\");
  const components = name.split(/[\\/]/u);
  if (directory) components.pop();
  if (components.length === 0 || components.length > 128) {
    throw new Error(`The verified release archive contains an invalid entry: ${JSON.stringify(name)}`);
  }
  for (const component of components) {
    if (
      component.length === 0 || component.length > 255 || component === "." || component === ".." ||
      WINDOWS_INVALID_COMPONENT.test(component) || WINDOWS_RESERVED_NAME.test(component)
    ) {
      throw new Error(`The verified release archive contains an unsafe Windows path: ${JSON.stringify(name)}`);
    }
  }
  const normalized = components.join("/").toLowerCase();
  if (seen.has(normalized)) {
    throw new Error(`The verified release archive contains case-insensitive duplicate paths: ${name}`);
  }
  seen.add(normalized);
  return { path: normalized, directory };
}

/** Read the entire authenticated ZIP directory before either Windows extractor runs. */
async function inspectZipEntries(archivePath) {
  const handle = await open(archivePath, "r");
  try {
    const metadata = await handle.stat();
    if (!metadata.isFile() || metadata.size < 22 || metadata.size > MAX_ARCHIVE_BYTES) {
      throw new Error("The verified release archive has an invalid or oversized ZIP payload.");
    }
    const tailSize = Math.min(metadata.size, 65_557);
    const tail = Buffer.alloc(tailSize);
    await handle.read(tail, 0, tailSize, metadata.size - tailSize);
    let end = -1;
    for (let index = tail.length - 22; index >= 0; index -= 1) {
      if (tail.readUInt32LE(index) === 0x06054b50 && index + 22 + tail.readUInt16LE(index + 20) === tail.length) {
        end = index;
        break;
      }
    }
    if (end < 0) throw new Error("The verified release archive has no valid ZIP central directory.");
    const disk = tail.readUInt16LE(end + 4);
    const centralDisk = tail.readUInt16LE(end + 6);
    const diskEntries = tail.readUInt16LE(end + 8);
    const entryCount = tail.readUInt16LE(end + 10);
    const centralSize = tail.readUInt32LE(end + 12);
    const centralOffset = tail.readUInt32LE(end + 16);
    if (
      disk !== 0 || centralDisk !== 0 || diskEntries !== entryCount ||
      entryCount === 0 || entryCount === 0xffff || entryCount > MAX_ARCHIVE_ENTRIES ||
      centralSize === 0xffff_ffff || centralOffset === 0xffff_ffff ||
      centralSize > MAX_CENTRAL_DIRECTORY_BYTES ||
      centralOffset + centralSize > metadata.size - (tail.length - end)
    ) {
      throw new Error("The verified release archive uses an unsupported or unsafe ZIP layout.");
    }
    const central = Buffer.alloc(centralSize);
    await handle.read(central, 0, centralSize, centralOffset);
    let offset = 0;
    let extractedBytes = 0;
    const names = new Set();
    const manifest = [];
    for (let index = 0; index < entryCount; index += 1) {
      if (offset + 46 > central.length || central.readUInt32LE(offset) !== 0x02014b50) {
        throw new Error("The verified release archive has a malformed ZIP central-directory entry.");
      }
      const flags = central.readUInt16LE(offset + 8);
      const compressedBytes = central.readUInt32LE(offset + 20);
      const uncompressedBytes = central.readUInt32LE(offset + 24);
      const nameBytes = central.readUInt16LE(offset + 28);
      const extraBytes = central.readUInt16LE(offset + 30);
      const commentBytes = central.readUInt16LE(offset + 32);
      const diskNumber = central.readUInt16LE(offset + 34);
      const attributes = central.readUInt32LE(offset + 38);
      const next = offset + 46 + nameBytes + extraBytes + commentBytes;
      if (
        next > central.length || diskNumber !== 0 || (flags & 1) !== 0 ||
        compressedBytes === 0xffff_ffff || uncompressedBytes === 0xffff_ffff
      ) {
        throw new Error("The verified release archive has an unsupported, encrypted, or ZIP64 entry.");
      }
      const unixType = (attributes >>> 16) & 0o170000;
      if (unixType === 0o120000 || (attributes & 0x0400) !== 0) {
        throw new Error("The verified release archive contains a symbolic link or Windows reparse point.");
      }
      const name = central.subarray(offset + 46, offset + 46 + nameBytes).toString("utf8");
      manifest.push({ ...validateArchiveName(name, names), size: uncompressedBytes });
      extractedBytes += uncompressedBytes;
      if (extractedBytes > MAX_EXTRACTED_BYTES) {
        throw new Error("The verified release archive exceeds its maximum extracted size.");
      }
      offset = next;
    }
    if (offset !== central.length) throw new Error("The verified release ZIP central directory contains trailing data.");
    const kinds = new Map(manifest.map((entry) => [entry.path, entry.directory]));
    for (const entry of manifest) {
      const components = entry.path.split("/");
      for (let index = 1; index < components.length; index += 1) {
        if (kinds.get(components.slice(0, index).join("/")) === false) {
          throw new Error("The verified release ZIP contains a file used as a directory.");
        }
      }
    }
    return { entries: entryCount, extractedBytes, manifest };
  } finally {
    await handle.close();
  }
}

/** Preserve the public, bounded ZIP-validation summary. */
export async function validateZipEntries(archivePath) {
  const { entries, extractedBytes } = await inspectZipEntries(archivePath);
  return { entries, extractedBytes };
}

async function defaultSha256File(filename) {
  const digest = createHash("sha256");
  for await (const chunk of createReadStream(filename)) digest.update(chunk);
  return digest.digest("hex");
}

async function defaultDownload(url, destination) {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), 30 * 60_000);
  try {
    let requested = new URL(url);
    let response;
    for (let redirects = 0; redirects < 8; redirects += 1) {
      if (
        requested.protocol !== "https:" ||
        (requested.hostname !== "github.com" &&
          requested.hostname !== "objects.githubusercontent.com" &&
          requested.hostname !== "release-assets.githubusercontent.com" &&
          !requested.hostname.endsWith(".githubusercontent.com"))
      ) {
        throw new Error(`Official release download refused an untrusted redirect: ${requested.href}`);
      }
      response = await fetch(requested, { redirect: "manual", signal: controller.signal });
      if (response.status < 300 || response.status >= 400) break;
      const location = response.headers.get("location");
      if (!location) throw new Error("Official release download returned a redirect without a destination.");
      requested = new URL(location, requested);
      response = undefined;
    }
    if (!response || !response.ok || response.body === null) {
      throw new Error(`Official release download failed safely: ${response?.status ?? "too many redirects"} ${url}`);
    }
    let total = 0;
    const boundSize = new Transform({
      transform(chunk, _encoding, callback) {
        total += chunk.byteLength;
        callback(total > MAX_ARCHIVE_BYTES ? new Error("The official release archive exceeds its size limit.") : null, chunk);
      },
    });
    await pipeline(Readable.fromWeb(response.body), boundSize, createWriteStream(destination, { flags: "wx", mode: 0o600 }));
  } finally {
    clearTimeout(timeout);
  }
}

async function defaultRunCommand(command, arguments_, options) {
  const result = await execFileAsync(command, arguments_, {
    cwd: options.cwd,
    env: options.env,
    windowsHide: true,
    shell: false,
    encoding: "utf8",
    timeout: options.timeout ?? 30 * 60_000,
    maxBuffer: 16 * 1024 * 1024,
  });
  return { stdout: result.stdout, stderr: result.stderr };
}

async function withProgress(context, label, operation) {
  const started = Date.now();
  let updates = 0;
  const elapsed = () => `${Math.round((Date.now() - started) / 1_000)}s`;
  context.logger(`${label}...`);
  const timer = setInterval(() => {
    if (updates < MAX_PROGRESS_UPDATES) {
      updates += 1;
      context.logger(`${label}: still running (${elapsed()}).`);
    }
  }, PROGRESS_INTERVAL_MS);
  timer.unref?.();
  try {
    const result = await operation();
    context.logger(`${label}: completed (${elapsed()}).`);
    return result;
  } catch (error) {
    context.logger(`${label}: failed (${elapsed()}).`);
    throw error;
  } finally {
    clearInterval(timer);
  }
}

function caseInsensitiveValue(environment, requested) {
  const normalized = requested.toUpperCase();
  const match = Object.entries(environment).find(([name]) => name.toUpperCase() === normalized);
  return match?.[1];
}

async function trustedExecutable(candidate, label, context) {
  if (!path.isAbsolute(candidate)) throw new Error(`${label} must use a trusted absolute executable path.`);
  const canonical = await realpath(candidate);
  await requireRealFile(canonical, label, context.platform);
  for (const untrustedRoot of [context.pluginRoot, context.toolchainRoot, context.localRoot, context.workingDirectory]) {
    if (untrustedRoot !== undefined && withinRoot(untrustedRoot, canonical)) {
      throw new Error(`${label} must not execute from an untrusted project or toolchain: ${canonical}`);
    }
  }
  return canonical;
}

async function resolveGitExecutable(context) {
  if (context.configuredGitExecutable !== undefined) {
    return trustedExecutable(context.configuredGitExecutable, "The trusted Git executable", context);
  }
  const configuredPath = caseInsensitiveValue(context.environment, "PATH") ?? "";
  for (const directory of configuredPath.split(path.delimiter)) {
    if (!directory || !path.isAbsolute(directory)) continue;
    const candidate = path.join(directory, "git.exe");
    try {
      return await trustedExecutable(candidate, "The trusted Git executable", context);
    } catch {
      // Reject missing aliases and any Git executable inside the checkout, vendor, or project.
    }
  }
  throw new Error("ModRetro Chromatic setup requires a trusted Git installation outside the selected project and toolchain.");
}

async function resolvePowerShell(environment, context, override) {
  const systemRoot = context.systemRoot ?? caseInsensitiveValue(environment, "SystemRoot") ?? "C:\\Windows";
  const candidate = override ?? path.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
  if (override === undefined) {
    await requireRealFile(candidate, "The trusted Windows PowerShell executable", context.platform);
  }
  return trustedExecutable(candidate, "The trusted Windows PowerShell executable", context);
}

async function resolveWindowsTar(context) {
  if (context.systemRoot === undefined) {
    throw new Error("Windows ZIP extraction requires a trusted SystemRoot to locate System32/tar.exe.");
  }
  const candidate = path.join(context.systemRoot, "System32", "tar.exe");
  await requireRealFile(candidate, "The trusted Windows System32 tar.exe", context.platform);
  return trustedExecutable(candidate, "The trusted Windows System32 tar.exe", context);
}

/** Each extractor gets an owned, empty directory; failed attempts are never mixed. */
async function extractionAttempt(destination, manifest, context, extract) {
  const staging = await mkdtemp(path.join(path.dirname(destination), ".extract-attempt-"));
  try {
    try {
      await extract(staging);
    } catch (error) {
      return { error };
    }
    // Validation failures are not extractor failures and must not trigger a fallback.
    await assertExtractedArchiveComplete(staging, manifest, context.platform);
    await requireRealDirectory(destination, "The empty release extraction destination", context.platform);
    if ((await readdir(destination)).length !== 0) {
      throw new Error("The release extraction destination is no longer empty; nothing was installed.");
    }
    for (const entry of await readdir(staging)) {
      await rename(path.join(staging, entry), path.join(destination, entry));
    }
    return {};
  } finally {
    await rm(staging, { recursive: true, force: true });
  }
}

async function defaultExtractArchive(archivePath, destination, options) {
  const manifest = options.archiveManifest ?? (await inspectZipEntries(archivePath)).manifest;
  const script = [
    "$ErrorActionPreference = 'Stop'",
    "$null = [System.Reflection.Assembly]::Load('System.IO.Compression.FileSystem')",
    "[System.IO.Compression.ZipFile]::ExtractToDirectory($env:CODEX_GBS_ARCHIVE_PATH, $env:CODEX_GBS_DESTINATION_PATH)",
  ].join("\n");
  const encoded = Buffer.from(script, "utf16le").toString("base64");
  const powershell = await extractionAttempt(destination, manifest, options, async (staging) => {
    const executable = await resolvePowerShell(options.environment, options, options.powershellPath);
    await options.runCommand(executable, ["-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand", encoded], {
      cwd: options.scriptDirectory,
      env: {
        ...options.environment,
        CODEX_GBS_ARCHIVE_PATH: archivePath,
        CODEX_GBS_DESTINATION_PATH: staging,
      },
    });
  });
  if (!("error" in powershell)) return;

  options.logger("PowerShell ZIP extraction was unavailable or failed; retrying the verified archive with Windows System32 tar.exe.");
  const tar = await extractionAttempt(destination, manifest, options, async (staging) => {
    const executable = await resolveWindowsTar(options);
    await options.runCommand(executable, ["-xf", archivePath, "-C", staging], {
      cwd: options.scriptDirectory,
      env: options.environment,
    });
  });
  if ("error" in tar) {
    throw new Error(
      "The verified release could not be extracted by Windows PowerShell or trusted System32/tar.exe. Repair either Windows component, then rerun setup.",
      { cause: new AggregateError([powershell.error, tar.error]) },
    );
  }
}

async function ensureVerifiedArchive({ root, downloads, release, url, platform, download, sha256File }) {
  await ensureManagedDirectory(root, downloads, platform);
  const destination = path.join(downloads, release.archive);
  await assertManagedPathSafe(root, destination, platform);
  if (!(await metadataIfPresent(destination))) {
    const temporary = path.join(downloads, `.download-${randomUUID()}`);
    try {
      await download(url, temporary);
      await requireRealFile(temporary, "The downloaded official release archive", platform);
      const actual = await sha256File(temporary);
      if (actual !== release.sha256) {
        throw new Error(`Official release SHA-256 verification failed; archive was not installed: ${release.archive}`);
      }
      await rename(temporary, destination);
    } finally {
      await rm(temporary, { force: true }).catch(() => {});
    }
  }
  await requireRealFile(destination, "The cached official release archive", platform);
  if ((await sha256File(destination)) !== release.sha256) {
    throw new Error(`Official release SHA-256 verification failed; cached archive was not installed: ${release.archive}`);
  }
  const { manifest } = await inspectZipEntries(destination);
  return { archive: destination, manifest };
}

async function assertExtractedTreeSafe(root, platform, depth = 0, visit, relativeRoot = root) {
  if (depth > 128) throw new Error("The extracted release archive exceeds its maximum directory depth.");
  for (const entry of await readdir(root, { withFileTypes: true })) {
    const candidate = path.join(root, entry.name);
    const metadata = await lstat(candidate);
    if (metadata.isSymbolicLink()) {
      throw new Error(`The extracted release archive contains a symbolic link or junction: ${candidate}`);
    }
    if (!samePath(await realpath(candidate), candidate, platform)) {
      throw new Error(`The extracted release archive contains a redirected filesystem entry: ${candidate}`);
    }
    if (visit) visit(path.relative(relativeRoot, candidate).split(path.sep).join("/").toLowerCase(), metadata);
    if (metadata.isDirectory()) await assertExtractedTreeSafe(candidate, platform, depth + 1, visit, relativeRoot);
    else if (!metadata.isFile()) throw new Error(`The extracted release archive contains a non-file: ${candidate}`);
  }
}

async function assertExtractedArchiveComplete(root, manifest, platform) {
  const expected = new Map();
  for (const entry of manifest) {
    const components = entry.path.split("/");
    for (let index = 1; index < components.length; index += 1) {
      expected.set(components.slice(0, index).join("/"), { directory: true });
    }
    expected.set(entry.path, entry);
  }
  const remaining = new Set(expected.keys());
  await assertExtractedTreeSafe(root, platform, 0, (relative, metadata) => {
    const entry = expected.get(relative);
    if (!entry) {
      throw new Error(`The extracted release archive contains an undeclared entry: ${relative}`);
    }
    if (!remaining.has(relative)) {
      throw new Error(`The extracted release archive contains a case-insensitive duplicate entry: ${relative}`);
    }
    if (entry.directory !== metadata.isDirectory() || (!entry.directory && entry.size !== metadata.size)) {
      throw new Error(`The extracted release archive is incomplete or has an incorrect entry size: ${relative}`);
    }
    remaining.delete(relative);
  });
  if (remaining.size !== 0) {
    throw new Error(`The extracted release archive is incomplete; missing entry: ${remaining.values().next().value}`);
  }
}

async function discoverDesktopExecutable(directory, platform, depth = 0) {
  if (depth > 5) return undefined;
  for (const entry of await readdir(directory, { withFileTypes: true })) {
    const candidate = path.join(directory, entry.name);
    if (entry.isSymbolicLink()) throw new Error(`The GB Studio desktop application contains a symbolic link: ${candidate}`);
    if (entry.isFile() && ["gb-studio.exe", "gb studio.exe"].includes(entry.name.toLowerCase())) {
      return requireRealFile(candidate, "The official GB Studio desktop application", platform);
    }
    if (entry.isDirectory()) {
      const found = await discoverDesktopExecutable(candidate, platform, depth + 1);
      if (found) return found;
    }
  }
  return undefined;
}

const ORIGINAL_CLI_TEST = 'const isCli = __dirname.indexOf("out/cli") > -1;';
const ORIGINAL_CLI_ROOT = 'rootDir = __dirname.substring(0, __dirname.lastIndexOf("out/cli"));';
const PATCHED_CLI_TEST = 'const normalizedCliDirectory = __dirname.replace(/\\\\/g, "/");\nconst isCli = normalizedCliDirectory.indexOf("out/cli") > -1;';
const PATCHED_CLI_ROOT = 'rootDir = __dirname.substring(0, normalizedCliDirectory.lastIndexOf("out/cli"));';

function patchedWindowsCliSource(original) {
  if (
    original.split(ORIGINAL_CLI_TEST).length !== 2 ||
    original.split(ORIGINAL_CLI_ROOT).length !== 2 ||
    original.includes("normalizedCliDirectory")
  ) {
    throw new Error("The pinned GB Studio CLI constants do not match the expected Windows compatibility patch.");
  }
  return original.replace(ORIGINAL_CLI_TEST, PATCHED_CLI_TEST).replace(ORIGINAL_CLI_ROOT, PATCHED_CLI_ROOT);
}

/** Patch only the exact verified GB Studio 4.3.2 CLI Windows-separator defect. */
export async function patchWindowsCliConstants(constantsPath, options = {}) {
  const platform = options.platform ?? process.platform;
  await requireRealFile(constantsPath, "The verified GB Studio constants source", platform);
  const original = await readFile(constantsPath, "utf8");
  if (original.includes(PATCHED_CLI_TEST) && original.includes(PATCHED_CLI_ROOT)) return false;
  const updated = patchedWindowsCliSource(original);
  await writeFile(constantsPath, updated, { encoding: "utf8", flag: "r+" });
  return true;
}

async function resolveCorepackScript(executable, context, override) {
  if (override !== undefined) return trustedExecutable(override, "The trusted Corepack JavaScript launcher", context);
  const directory = path.dirname(executable);
  const candidates = [
    path.join(directory, "node_modules", "corepack", "dist", "corepack.js"),
    path.join(directory, "..", "lib", "node_modules", "corepack", "dist", "corepack.js"),
    path.join(directory, "node_modules", "corepack", "dist", "corepack.cjs"),
  ];
  // `npm install --global corepack` may use a separate Windows npm prefix.
  // Execute its JavaScript with trusted Node, never a PATH-provided .cmd shim.
  const configuredPath = caseInsensitiveValue(context.environment, "PATH") ?? "";
  for (const entry of configuredPath.split(path.delimiter)) {
    const prefix = entry.trim().replace(/^"(.*)"$/u, "$1");
    if (!prefix || !path.isAbsolute(prefix)) continue;
    candidates.push(path.join(prefix, "node_modules", "corepack", "dist", "corepack.js"));
    candidates.push(path.join(prefix, "node_modules", "corepack", "dist", "corepack.cjs"));
  }
  for (const candidate of new Set(candidates)) {
    if (await metadataIfPresent(candidate)) {
      try {
        return await trustedExecutable(path.resolve(candidate), "The trusted Corepack JavaScript launcher", context);
      } catch {
        // A project-local or redirected launcher must not shadow a trusted installation.
      }
    }
  }
  throw new Error(
    "ModRetro Chromatic setup could not find trusted Corepack beside Node.js or on an absolute PATH entry. " +
      COREPACK_REMEDIATION,
  );
}

function commandOutput(result) {
  if (typeof result === "string") return result.trim();
  return String(result?.stdout ?? "").trim();
}

function managedCorepackEnvironment(context) {
  return {
    ...context.environment,
    COREPACK_HOME: context.corepackHome,
    COREPACK_DEFAULT_TO_LATEST: "0",
    COREPACK_ENABLE_AUTO_PIN: "0",
    COREPACK_ENABLE_DOWNLOAD_PROMPT: "0",
    COREPACK_ENABLE_PROJECT_SPEC: "1",
    COREPACK_ENABLE_STRICT: "1",
    COREPACK_ENABLE_UNSAFE_CUSTOM_URLS: "0",
    COREPACK_ENV_FILE: "0",
  };
}

async function verifyCorepack(corepack, context) {
  try {
    const version = commandOutput(await context.runCommand(context.nodeExecutable, [corepack, "--version"], {
      cwd: context.scriptDirectory,
      env: managedCorepackEnvironment(context),
      timeout: 15_000,
    }));
    if (!/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)*$/u.test(version)) {
      throw new Error("The Corepack launcher did not report a valid version.");
    }
  } catch (error) {
    throw new Error(`The trusted Corepack launcher could not run with the selected Node.js. ${COREPACK_REMEDIATION}`, { cause: error });
  }
}

async function verifyCompiler(compiler, header, context) {
  await requireRealFile(compiler, "The GBDK 4.5.0 Windows compiler", context.platform);
  await requireRealFile(header, "The GBDK 4.5.0 version header", context.platform);
  const source = await readFile(header, "utf8");
  if (!/^\s*#\s*define\s+__GBDK_VERSION\s+450(?:\s|$)/mu.test(source)) {
    throw new Error(`The existing GBDK installation does not report the required 4.5.0 release: ${header}`);
  }
  await context.runCommand(compiler, ["-v"], { cwd: context.scriptDirectory, env: context.environment });
}

async function verifyCli(cli, context) {
  await requireRealFile(cli, "The official GB Studio 4.3.2 CLI", context.platform);
  const output = commandOutput(await context.runCommand(context.nodeExecutable, [cli, "--version"], {
    cwd: context.scriptDirectory,
    env: context.environment,
  }));
  if (output !== WINDOWS_TOOLCHAIN_RELEASES.gbStudio.version && output !== `v${WINDOWS_TOOLCHAIN_RELEASES.gbStudio.version}`) {
    throw new Error(`The official GB Studio CLI reported ${output}; expected ${WINDOWS_TOOLCHAIN_RELEASES.gbStudio.version}.`);
  }
}

async function installGbdk(local, paths, context) {
  await assertManagedPathSafe(local, paths.gbdkRoot, context.platform);
  if (await metadataIfPresent(paths.compiler)) {
    await verifyCompiler(paths.compiler, paths.versionHeader, context);
    return;
  }
  if (await metadataIfPresent(paths.gbdkRoot)) {
    throw new Error(`The existing GBDK installation is incomplete; repair it manually: ${paths.gbdkRoot}`);
  }
  const { archive, manifest } = await withProgress(context, `Preparing verified GBDK ${WINDOWS_TOOLCHAIN_RELEASES.gbdk.version} archive`, () => ensureVerifiedArchive({
    root: local,
    downloads: paths.downloads,
    release: WINDOWS_TOOLCHAIN_RELEASES.gbdk,
    url: `https://github.com/gbdk-2020/gbdk-2020/releases/download/${WINDOWS_TOOLCHAIN_RELEASES.gbdk.version}/${WINDOWS_TOOLCHAIN_RELEASES.gbdk.archive}`,
    platform: context.platform,
    download: context.download,
    sha256File: context.sha256File,
  }));
  const staging = await mkdtemp(path.join(local, ".extract-gbdk-"));
  try {
    await withProgress(context, `Extracting verified GBDK ${WINDOWS_TOOLCHAIN_RELEASES.gbdk.version}`, async () => {
      await context.extractArchive(archive, staging, { ...context, archiveManifest: manifest });
      await assertExtractedArchiveComplete(staging, manifest, context.platform);
    });
    const extracted = path.join(staging, "gbdk");
    await requireRealDirectory(extracted, "The extracted GBDK release", context.platform);
    await requireRealFile(path.join(extracted, "bin", "lcc.exe"), "The extracted GBDK Windows compiler", context.platform);
    await rename(extracted, paths.gbdkRoot);
  } finally {
    await rm(staging, { recursive: true, force: true });
  }
  await verifyCompiler(paths.compiler, paths.versionHeader, context);
}

async function installDesktop(local, paths, context) {
  await ensureManagedDirectory(local, paths.apps, context.platform);
  await assertManagedPathSafe(local, paths.desktopRoot, context.platform);
  if (await metadataIfPresent(paths.desktopRoot)) {
    await requireRealDirectory(paths.desktopRoot, "The existing GB Studio desktop application", context.platform);
    const executable = await discoverDesktopExecutable(paths.desktopRoot, context.platform);
    if (!executable) throw new Error("The existing GB Studio desktop application does not contain gb-studio.exe or GB Studio.exe.");
    return executable;
  }
  const { archive, manifest } = await withProgress(context, `Preparing verified GB Studio ${WINDOWS_TOOLCHAIN_RELEASES.gbStudio.version} desktop archive`, () => ensureVerifiedArchive({
    root: local,
    downloads: paths.downloads,
    release: WINDOWS_TOOLCHAIN_RELEASES.gbStudio,
    url: `https://github.com/chrismaltby/gb-studio/releases/download/v${WINDOWS_TOOLCHAIN_RELEASES.gbStudio.version}/${WINDOWS_TOOLCHAIN_RELEASES.gbStudio.archive}`,
    platform: context.platform,
    download: context.download,
    sha256File: context.sha256File,
  }));
  const staging = await mkdtemp(path.join(paths.apps, ".extract-desktop-"));
  try {
    await withProgress(context, `Extracting verified GB Studio ${WINDOWS_TOOLCHAIN_RELEASES.gbStudio.version} desktop`, async () => {
      await context.extractArchive(archive, staging, { ...context, archiveManifest: manifest });
      await assertExtractedArchiveComplete(staging, manifest, context.platform);
    });
    if (!(await discoverDesktopExecutable(staging, context.platform))) {
      throw new Error("The verified GB Studio Windows standalone release did not contain gb-studio.exe or GB Studio.exe.");
    }
    await rename(staging, paths.desktopRoot);
  } finally {
    await rm(staging, { recursive: true, force: true });
  }
  return discoverDesktopExecutable(paths.desktopRoot, context.platform);
}

function isSchannelCredentialsFailure(error) {
  const description = [error?.message, error?.stderr, error?.stdout].map((value) => String(value ?? "")).join("\n");
  return /\bschannel\b/iu.test(description) && /\bSEC_E_NO_CREDENTIALS\b/u.test(description);
}

async function runTrustedGit(git, arguments_, context, cwd, options = {}) {
  const securityOptions = [
    "-c", `core.hooksPath=${context.disabledHooksDirectory}`,
    "-c", "core.fsmonitor=false",
  ];
  const run = async (argumentsToRun) => {
    await requireRealDirectory(context.disabledHooksDirectory, "The disabled Git hooks directory", context.platform);
    if ((await readdir(context.disabledHooksDirectory)).length !== 0) {
      throw new Error("The trusted disabled Git hooks directory must remain empty.");
    }
    return context.runCommand(git, [...securityOptions, ...argumentsToRun], { cwd, env: context.environment });
  };
  try {
    return await run(arguments_);
  } catch (error) {
    if (!options.network || context.platform !== "win32" || !isSchannelCredentialsFailure(error)) throw error;
    context.logger("Git Schannel credentials are unavailable; retrying this network operation once with OpenSSL and normal TLS verification.");
    const retryArguments = options.retryArguments?.() ?? arguments_;
    return run(["-c", "http.sslBackend=openssl", ...retryArguments]);
  }
}

function normalizeSourceNewlines(value) {
  return value.replace(/\r\n/g, "\n");
}

async function verifyPinnedWorktree(git, paths, context) {
  const changes = commandOutput(await runTrustedGit(
    git,
    ["diff", "--no-ext-diff", "--name-only", "HEAD", "--"],
    context,
    paths.vendor,
  )).split(/\r?\n/u).map((entry) => entry.trim()).filter(Boolean);
  if (changes.length > 1 || (changes.length === 1 && changes[0] !== "src/consts.ts")) {
    throw new Error(`The pinned GB Studio checkout contains untrusted tracked changes: ${changes.join(", ")}`);
  }
  const pristineResult = await runTrustedGit(git, ["show", "HEAD:src/consts.ts"], context, paths.vendor);
  const pristine = normalizeSourceNewlines(typeof pristineResult === "string" ? pristineResult : String(pristineResult.stdout ?? ""));
  const expectedPatch = patchedWindowsCliSource(pristine);
  const constantsPath = path.join(paths.vendor, "src", "consts.ts");
  await requireRealFile(constantsPath, "The verified GB Studio constants source", context.platform);
  const actual = normalizeSourceNewlines(await readFile(constantsPath, "utf8"));
  if (changes.length === 0 ? actual !== pristine : actual !== expectedPatch) {
    throw new Error("The pinned GB Studio constants contain changes beyond the exact approved Windows compatibility patch.");
  }
}

async function ensureOfficialCli(local, paths, context) {
  await assertManagedPathSafe(local, paths.vendor, context.platform);
  if (await metadataIfPresent(paths.cli)) {
    await verifyCli(paths.cli, context);
    return;
  }
  await ensureManagedDirectory(local, path.dirname(paths.vendor), context.platform);
  const git = context.gitExecutable ?? await resolveGitExecutable(context);
  if (!(await metadataIfPresent(paths.vendor))) {
    // Git can leave a partial clone behind after a TLS failure. Each retry owns
    // a fresh destination; never remove or reset a preexisting vendor checkout.
    const staging = await mkdtemp(path.join(path.dirname(paths.vendor), ".clone-gb-studio-"));
    let cloned = path.join(staging, "checkout");
    const cloneArguments = () => [
      "clone", "--depth", "1", "--branch", `v${WINDOWS_TOOLCHAIN_RELEASES.gbStudio.version}`,
      "https://github.com/chrismaltby/gb-studio.git", cloned,
    ];
    try {
      await withProgress(context, `Fetching official GB Studio ${WINDOWS_TOOLCHAIN_RELEASES.gbStudio.version} source`, () => runTrustedGit(
        git,
        cloneArguments(),
        context,
        context.scriptDirectory,
        {
          network: true,
          retryArguments: () => {
            cloned = path.join(staging, "checkout-openssl");
            return cloneArguments();
          },
        },
      ));
      await requireRealDirectory(cloned, "The downloaded official GB Studio checkout", context.platform);
      if (await metadataIfPresent(paths.vendor)) {
        throw new Error(`The GB Studio vendor destination appeared during setup; it was not replaced: ${paths.vendor}`);
      }
      await rename(cloned, paths.vendor);
    } finally {
      await rm(staging, { recursive: true, force: true });
    }
  }
  await requireRealDirectory(paths.vendor, "The official GB Studio checkout", context.platform);
  await requireRealDirectory(path.join(paths.vendor, ".git"), "The official GB Studio Git checkout", context.platform);
  const head = commandOutput(await runTrustedGit(git, ["rev-parse", "HEAD"], context, paths.vendor));
  if (head !== WINDOWS_TOOLCHAIN_RELEASES.gbStudio.commit) {
    throw new Error(`The official GB Studio checkout has commit ${head}; expected ${WINDOWS_TOOLCHAIN_RELEASES.gbStudio.commit}.`);
  }
  // A trusted HEAD does not authenticate a mutable preexisting worktree.
  await verifyPinnedWorktree(git, paths, context);
  await withProgress(context, "Fetching pinned GBVM engine source", () => runTrustedGit(
    git,
    ["submodule", "update", "--init", "--depth", "1", "appData/engine/gbvm"],
    context,
    paths.vendor,
    { network: true },
  ));
  const gbvmHead = commandOutput(await runTrustedGit(
    git,
    ["-C", "appData/engine/gbvm", "rev-parse", "HEAD"],
    context,
    paths.vendor,
  ));
  if (gbvmHead !== WINDOWS_TOOLCHAIN_RELEASES.gbStudio.gbvmCommit) {
    throw new Error(`The official GBVM source has commit ${gbvmHead}; expected ${WINDOWS_TOOLCHAIN_RELEASES.gbStudio.gbvmCommit}.`);
  }
  await verifyPinnedWorktree(git, paths, context);

  const platformTools = path.join(paths.vendor, "buildTools", WINDOWS_TOOLCHAIN_RELEASES.platform);
  await requireRealDirectory(path.join(paths.vendor, "buildTools"), "The official GB Studio build-tools root", context.platform);
  await requireRealDirectory(platformTools, "The official GB Studio Windows x64 build tools", context.platform);
  const copiedGbdk = path.join(platformTools, "gbdk");
  if (await metadataIfPresent(copiedGbdk)) {
    await requireRealDirectory(copiedGbdk, "The official GB Studio Windows GBDK directory", context.platform);
    await requireRealFile(path.join(copiedGbdk, "bin", "lcc.exe"), "The official GB Studio Windows GBDK compiler", context.platform);
  } else {
    await cp(paths.gbdkRoot, copiedGbdk, { recursive: true, force: false, errorOnExist: true, verbatimSymlinks: false });
    await assertExtractedTreeSafe(copiedGbdk, context.platform);
  }

  // Pin both upstream Git identities before making the one narrow source compatibility fix.
  await patchWindowsCliConstants(path.join(paths.vendor, "src", "consts.ts"), { platform: context.platform });
  await verifyPinnedWorktree(git, paths, context);

  const corepack = context.corepackScript ?? await resolveCorepackScript(context.nodeExecutable, context, context.corepackPath);
  // Corepack trusts metadata in an existing executable cache without rehashing
  // the cached package-manager bytes. Never execute a preexisting cached Yarn.
  await assertManagedPathSafe(local, context.corepackHome, context.platform);
  await mkdir(context.corepackHome, { mode: 0o700 });
  await requireRealDirectory(context.corepackHome, "The fresh owned Corepack cache", context.platform);
  await ensureManagedDirectory(local, paths.yarnGlobal, context.platform);
  const yarnEnvironment = {
    ...managedCorepackEnvironment(context),
    YARN_ENABLE_GLOBAL_CACHE: "true",
    YARN_GLOBAL_FOLDER: paths.yarnGlobal,
    ELECTRON_SKIP_BINARY_DOWNLOAD: "1",
  };
  await withProgress(context, "Installing pinned GB Studio dependencies", () => context.runCommand(context.nodeExecutable, [corepack, "yarn", "install", "--immutable"], {
    cwd: paths.vendor,
    env: yarnEnvironment,
  }));

  const electron = path.join(paths.vendor, "node_modules", "electron");
  await requireRealDirectory(electron, "The official GB Studio Electron dependency", context.platform);
  const electronPath = path.join(electron, "path.txt");
  const existingElectronPath = await metadataIfPresent(electronPath);
  if (existingElectronPath && (existingElectronPath.isSymbolicLink() || !existingElectronPath.isFile())) {
    throw new Error(`Official GB Studio Electron metadata must be a real regular file: ${electronPath}`);
  }
  if (!existingElectronPath) await writeFile(electronPath, "electron", { flag: "wx" });

  const webpack = path.join(paths.vendor, "node_modules", "webpack", "bin", "webpack.js");
  await requireRealFile(webpack, "The official GB Studio webpack compiler", context.platform);
  await withProgress(context, "Compiling the official GB Studio CLI", () => context.runCommand(context.nodeExecutable, [webpack, "--config", "./src/apps/gb-studio-cli/webpack.cli.config.js"], {
    cwd: paths.vendor,
    env: { ...yarnEnvironment, NO_TYPE_CHECKING: "1" },
  }));
  await verifyCli(paths.cli, context);
}

function windowsSetupSelection(options) {
  const arguments_ = options.args ?? [];
  const selected = parseWindowsSetupArguments(arguments_);
  const platform = options.platform ?? process.platform;
  const architecture = options.arch ?? process.arch;
  if (!selected.help && !selected.dryRun && (platform !== "win32" || architecture !== "x64")) {
    throw new Error(`Experimental native ModRetro Chromatic setup supports Windows x64 only; received ${platform} ${architecture}.`);
  }
  const logger = options.logger ?? ((message) => process.stdout.write(`${message}\n`));
  return { platform, selected, logger };
}

async function prepareWindowsSetup(options, selected, platform, logger) {
  const environment = options.environment ?? process.env;
  // Reject ambiguous PATH/SystemRoot/GB_* aliases before trusting any Windows setting.
  const sanitizedEnvironment = sanitizeWindowsBootstrapEnvironment(environment);
  const pluginRoot = await realpath(path.resolve(options.pluginRoot ?? PLUGIN_ROOT));
  const requestedToolchain = options.toolchainRoot ??
    caseInsensitiveValue(sanitizedEnvironment, "GB_STUDIO_TOOLCHAIN_ROOT") ?? pluginRoot;
  const toolchainRoot = await realpath(path.resolve(requestedToolchain));
  await requireRealDirectory(toolchainRoot, "The selected game toolchain root", platform);
  let local = path.join(toolchainRoot, ".local");
  const existingLocal = await metadataIfPresent(local);
  if (existingLocal?.isSymbolicLink()) {
    local = await realpath(local);
    if (path.basename(local).toLowerCase() !== ".local") {
      throw new Error(`A linked game toolchain must resolve to an actual .local directory: ${local}`);
    }
    await requireRealDirectory(local, "The linked game toolchain", platform);
  } else if (existingLocal && !existingLocal.isDirectory()) {
    throw new Error(`The game toolchain .local path is not a directory: ${local}`);
  }
  if (!existingLocal && selected.useExisting) {
    throw new Error(`The existing game toolchain .local directory is unavailable: ${local}`);
  }
  if (existingLocal) {
    local = await requireRealDirectory(local, "The game toolchain .local directory", platform);
  }
  if (!selected.useExisting) {
    await assertLegacySetupMutableRoot(toolchainRoot);
    await assertLegacySetupMutableRoot(local);
  }

  const paths = {
    downloads: path.join(local, "downloads"),
    apps: path.join(local, "apps"),
    desktopRoot: path.join(local, "apps", "GB Studio"),
    gbdkRoot: path.join(local, "gbdk"),
    compiler: path.join(local, "gbdk", "bin", "lcc.exe"),
    versionHeader: path.join(local, "gbdk", "include", "gbdk", "version.h"),
    vendor: path.join(local, "vendor", "gb-studio"),
    cli: path.join(local, "vendor", "gb-studio", "out", "cli", "gb-studio-cli.js"),
    yarnGlobal: path.join(local, ".yarn", "global"),
    yarnCache: path.join(local, ".yarn", "cache"), // Preserve validation of an existing legacy cache.
  };
  for (const managed of [
    paths.downloads, paths.apps, paths.desktopRoot, paths.gbdkRoot, paths.vendor,
    paths.yarnGlobal, paths.yarnCache,
  ]) {
    await assertManagedPathSafe(local, managed, platform);
    if (await metadataIfPresent(managed)) {
      await requireRealDirectory(managed, "A managed game toolchain directory", platform);
    }
  }

  const context = {
    platform,
    pluginRoot,
    toolchainRoot,
    localRoot: local,
    workingDirectory: options.workingDirectory ?? process.cwd(),
    scriptDirectory: options.scriptDirectory ?? path.join(pluginRoot, "scripts"),
    nodeExecutable: options.nodeExecutable ?? process.execPath,
    configuredGitExecutable: options.gitExecutable,
    corepackPath: options.corepackPath,
    corepackHome: path.join(local, `.corepack-preflight-${randomUUID()}`),
    powershellPath: options.powershellPath,
    environment: sanitizedEnvironment,
    runCommand: options.runCommand ?? defaultRunCommand,
    download: options.download ?? defaultDownload,
    sha256File: options.sha256File ?? defaultSha256File,
    extractArchive: options.extractArchive ?? defaultExtractArchive,
    logger,
  };
  await assertManagedPathSafe(local, context.corepackHome, platform);
  if (await metadataIfPresent(context.corepackHome)) {
    throw new Error("The read-only Corepack preflight cache path must not already exist.");
  }
  await requireRealDirectory(context.scriptDirectory, "The trusted plugin script directory", platform);
  context.nodeExecutable = await trustedExecutable(context.nodeExecutable, "The trusted Node.js executable", context);
  const configuredSystemRoot = caseInsensitiveValue(sanitizedEnvironment, "SystemRoot");
  if (!configuredSystemRoot && process.platform === "win32") {
    throw new Error("Experimental Windows setup requires a trusted, explicitly available SystemRoot.");
  }
  if (configuredSystemRoot) {
    const systemRoot = await requireRealDirectory(configuredSystemRoot, "The trusted Windows SystemRoot", platform);
    context.systemRoot = systemRoot;
    context.commandShell = await trustedExecutable(
      path.join(systemRoot, "System32", "cmd.exe"),
      "The trusted Windows command interpreter",
      context,
    );
    context.environment.COMSPEC = context.commandShell;
  }
  return { environment, toolchainRoot, local, localExists: Boolean(existingLocal), paths, context };
}

async function resolveInstallPrerequisites(selected, paths, context) {
  if (!selected.withCli) return false;
  if (selected.useExisting || await metadataIfPresent(paths.cli)) {
    await verifyCli(paths.cli, context);
    return false;
  }
  context.gitExecutable = await resolveGitExecutable(context);
  context.corepackScript = await resolveCorepackScript(context.nodeExecutable, context, context.corepackPath);
  await verifyCorepack(context.corepackScript, context);
  return true;
}

/** Explicit executable prerequisite probes; --dry-run is a separate passive plan. */
export async function preflightWindowsGbStudio(options = {}) {
  const { platform, selected, logger } = windowsSetupSelection(options);
  if (selected.help) {
    logger(SETUP_USAGE);
    return { help: true };
  }
  if (selected.dryRun) return gbStudioSetupPlan(options, selected, logger);
  const { toolchainRoot, local, paths, context } = await prepareWindowsSetup(options, selected, platform, logger);
  const requiresCliBuild = await resolveInstallPrerequisites(selected, paths, context);
  return {
    platform: WINDOWS_TOOLCHAIN_RELEASES.platform,
    toolchainRoot,
    localRoot: local,
    cli: paths.cli,
    nodeExecutable: context.nodeExecutable,
    gitExecutable: context.gitExecutable,
    corepackScript: context.corepackScript,
    requiresCliBuild,
  };
}

/** Dependency-injected native Windows implementation; tests can run safely on non-Windows hosts. */
export async function setupWindowsGbStudio(options = {}) {
  const { platform, selected, logger } = windowsSetupSelection(options);
  if (selected.help) {
    logger(SETUP_USAGE);
    return { help: true };
  }
  if (selected.dryRun) return gbStudioSetupPlan(options, selected, logger);
  const { environment, local, localExists, paths, context } = await prepareWindowsSetup(options, selected, platform, logger);

  if (selected.useExisting) {
    await verifyCompiler(paths.compiler, paths.versionHeader, context);
    await verifyCli(paths.cli, context);
    let desktop;
    if (selected.withDesktop) {
      await requireRealDirectory(paths.desktopRoot, "The existing GB Studio desktop application", platform);
      desktop = await discoverDesktopExecutable(paths.desktopRoot, platform);
      if (!desktop) throw new Error("The existing GB Studio desktop application does not contain gb-studio.exe or GB Studio.exe.");
    }
    logger(`Verified existing official GB Studio ${WINDOWS_TOOLCHAIN_RELEASES.gbStudio.version} CLI: ${paths.cli}`);
    logger(`Verified existing GBDK ${WINDOWS_TOOLCHAIN_RELEASES.gbdk.version} compiler: ${paths.compiler}`);
    return { platform: WINDOWS_TOOLCHAIN_RELEASES.platform, compiler: paths.compiler, cli: paths.cli, desktop, reused: true };
  }

  logger("Checking trusted Windows bootstrap prerequisites...");
  await resolveInstallPrerequisites(selected, paths, context);
  if (!localExists) await mkdir(local);
  await requireRealDirectory(local, "The game toolchain .local directory", platform);
  const temporary = await mkdtemp(path.join(local, ".bootstrap-"));
  context.corepackHome = path.join(temporary, "corepack");
  context.environment = sanitizeWindowsBootstrapEnvironment(environment, temporary);
  if (context.commandShell) context.environment.COMSPEC = context.commandShell;
  try {
    context.disabledHooksDirectory = path.join(temporary, "disabled-git-hooks");
    await mkdir(context.disabledHooksDirectory);
    await installGbdk(local, paths, context);
    const desktop = selected.withDesktop ? await installDesktop(local, paths, context) : undefined;
    if (selected.withCli) await ensureOfficialCli(local, paths, context);
    logger(desktop
      ? `GB Studio ${WINDOWS_TOOLCHAIN_RELEASES.gbStudio.version} desktop editor: ${desktop}`
      : "GB Studio desktop editor: optional; headless project compilation requires no GUI.");
    logger(`GBDK ${WINDOWS_TOOLCHAIN_RELEASES.gbdk.version} compiler: ${paths.compiler}`);
    logger(`Official GB Studio ${WINDOWS_TOOLCHAIN_RELEASES.gbStudio.version} CLI: ${paths.cli}`);
    return { platform: WINDOWS_TOOLCHAIN_RELEASES.platform, compiler: paths.compiler, cli: paths.cli, desktop, reused: false };
  } finally {
    await rm(temporary, { recursive: true, force: true });
  }
}

function dispatchUnixSetup(arguments_, options = {}) {
  const script = path.join(options.pluginRoot ?? PLUGIN_ROOT, "scripts", "setup-compiler.sh");
  return new Promise((resolve, reject) => {
    // Preserve the tracked executable's existing #!/usr/bin/env bash semantics exactly.
    const child = spawn(script, arguments_, {
      cwd: options.cwd ?? process.cwd(),
      env: options.environment ?? process.env,
      stdio: "inherit",
      shell: false,
    });
    child.once("error", reject);
    child.once("exit", (code, signal) => {
      if (code !== null) resolve(code);
      else resolve(128 + (os.constants.signals[signal] ?? 1));
    });
  });
}

/** Plan/help are passive on every platform; real apply retains the platform-specific installers. */
export async function dispatchGbStudioSetup(arguments_ = process.argv.slice(2), options = {}) {
  const selected = parseWindowsSetupArguments(arguments_);
  const logger = options.logger ?? ((message) => process.stdout.write(`${message}\n`));
  if (selected.help) {
    logger(SETUP_USAGE);
    return 0;
  }
  if (selected.dryRun) {
    gbStudioSetupPlan({ ...options, args: arguments_ }, selected, logger);
    return 0;
  }
  const platform = options.platform ?? process.platform;
  if (platform === "darwin" || platform === "linux") {
    return (options.dispatchUnix ?? dispatchUnixSetup)(arguments_, options);
  }
  if (platform === "win32") {
    await setupWindowsGbStudio({ ...options, args: arguments_, platform });
    return 0;
  }
  throw new Error(`Unsupported ModRetro Chromatic setup platform: ${platform}.`);
}

if (process.argv[1] && samePath(process.argv[1], SCRIPT_PATH)) {
  try {
    process.exitCode = await dispatchGbStudioSetup();
  } catch (error) {
    process.stderr.write(`ModRetro Chromatic setup failed: ${error instanceof Error ? error.message : String(error)}\n`);
    process.exitCode = error instanceof SetupUsageError ? error.exitCode : 1;
  }
}

SHA-256: 4f2f13e985392a937c7ea33b5cebdcef80b319df908609e64c3ed34975d99ffb