← Files ModRetro Chromatic PluginARCHIVED FILE

scripts/prepare-plugin-payload.mjs

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

↓ Download file

#!/usr/bin/env node

/** Stage only the npm distribution, never a checkout's toolchain or dependencies. */
import { spawnSync } from "node:child_process";
import { createHash } from "node:crypto";
import {
  chmodSync, closeSync, constants, existsSync, fchmodSync, fstatSync, linkSync, lstatSync, mkdirSync, mkdtempSync,
  openSync, readFileSync, readdirSync, realpathSync, renameSync, rmdirSync, rmSync, statSync,
  writeFileSync,
} from "node:fs";
import path from "node:path";
import os from "node:os";
import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
import { deflateRawSync } from "node:zlib";
import { bundleMcp } from "./bundle-mcp.mjs";
import { setupCrc32 } from "./setup-io.mjs";
import {
  CHROMATIC_MEMBERS, CHROMATIC_PACKED, CHROMATIC_PACKED_V2, CHROMATIC_NOTICE_PACK, CHROMATIC_ARTWORK_PACK, CHROMATIC_ARTWORK_MEMBERS, CHROMATIC_STORAGE_SOURCES, CHROMATIC_ROOT, CHROMATIC_TARGETS, CHROMATIC_VERSION, chromaticRequiredPaths, chromaticTargetFromManifest, chromaticFormatFromManifest,
  requireChromaticHostTarget, verifyChromaticBundle, verifyChromaticRecords,
} from "./chromatic-runtime.mjs";

const PLUGIN_NAME = "modretro-chromatic";
const SCRIPT_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const RECEIPT_PATH = ".codex-plugin/local-payload.json";
const DISTRIBUTION_MANIFEST = "distribution.json";
const MAX_FILES = 4096;
// Include the universal native bundle, declarations, sample and device artwork.
// Keep copy limits aligned with setup-runtime.mjs and start-mcp.mjs.
const MAX_FILE_BYTES = 24 * 1024 * 1024;
const MAX_TOTAL_BYTES = 136 * 1024 * 1024;
const FORBIDDEN_DIRECTORIES = new Set([
  ".git", ".agents", ".codex", ".local", "node_modules", "vendor", "artifacts",
  ".cache", "__pycache__", "coverage", "save-states",
]);

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

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

function inspect(candidate) {
  try { return lstatSync(candidate); }
  catch (error) {
    if (error.code === "ENOENT" || error.code === "ENOTDIR") return undefined;
    throw error;
  }
}

function readObject(candidate, label) {
  const contents = readFileSync(candidate, "utf8");
  let value;
  try { value = JSON.parse(contents); }
  catch (cause) { throw new Error(`${label} could not be read as JSON: ${candidate}`, { cause }); }
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
    throw new Error(`${label} must be a JSON object: ${candidate}`);
  }
  return value;
}

function realDirectory(candidate, label) {
  const result = realpathSync(path.resolve(candidate));
  if (!statSync(result).isDirectory()) throw new Error(`${label} must be a directory: ${candidate}`);
  return result;
}

function assertUnlinkedPath(candidate, label) {
  const absolute = path.resolve(candidate);
  const parsed = path.parse(absolute);
  let current = parsed.root;
  for (const component of absolute.slice(parsed.root.length).split(path.sep).filter(Boolean)) {
    current = path.join(current, component);
    const metadata = inspect(current);
    if (metadata === undefined) continue;
    if (metadata.isSymbolicLink() || canonicalKey(realpathSync(current)) !== canonicalKey(current)) {
      throw new Error(`${label} must not traverse a symbolic link or directory junction: ${current}`);
    }
  }
  return absolute;
}

export function isForbiddenPayloadPath(filename) {
  if (typeof filename !== "string" || !filename || filename.length > 1024 || /[\\:<>"|?*\u0000-\u001f]/u.test(filename)) return true;
  const pieces = filename.split("/");
  if (pieces.length > 32 || pieces.some((part) => !part || part === "." || part === ".." || /[. ]$/u.test(part) || /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/iu.test(part))) return true;
  if (path.posix.isAbsolute(filename) || path.win32.isAbsolute(filename)) return true;
  const lower = pieces.map((part) => part.toLowerCase());
  if (lower.some((part) => FORBIDDEN_DIRECTORIES.has(part))) return true;
  if (["src", "test", "tests"].includes(lower[0])) return true;
  const basename = lower.at(-1);
  return basename === ".npmrc" || basename === ".ds_store" || basename === ".env" ||
    basename.startsWith(".env.") || /\.(?:sav|ram|rtc|state|pyc|pyo)$/u.test(basename) ||
    /\.test\.[cm]?[jt]sx?$/u.test(basename) || /\.(?:playtest|gbstate)\.json$/u.test(basename) ||
    /^dist\/(?:device-capture(?:-jobs)?|device-capture\/(?:client|media|protocol))\.(?:js|js\.map|d\.ts)$/u.test(filename) ||
    /^dist\/playtest\.(?:js|js\.map|d\.ts)$/u.test(filename) ||
    /^scripts\/dogfood-[^/]+\.mjs$/u.test(filename) || filename === "scripts/integration-smoke.mjs" || filename === RECEIPT_PATH;
}

function regularFileBytes(root, relative, limit = MAX_FILE_BYTES) {
  const filename = path.join(root, ...relative.split("/"));
  if (!isWithin(root, filename)) throw new Error(`Distribution member escapes its package: ${relative}`);
  assertUnlinkedPath(filename, "Distribution member");
  const before = lstatSync(filename);
  if (!before.isFile() || before.isSymbolicLink()) throw new Error(`Distribution member is not a regular file: ${relative}`);
  if (before.size > limit) throw new Error(`Distribution member exceeds ${limit} bytes: ${relative}`);
  const descriptor = openSync(filename, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
  try {
    const opened = fstatSync(descriptor);
    if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino || opened.size !== before.size) {
      throw new Error(`Distribution member changed while opening it: ${relative}`);
    }
    const contents = readFileSync(descriptor);
    const after = fstatSync(descriptor);
    if (contents.length !== before.size || after.size !== before.size || after.mtimeMs !== opened.mtimeMs) {
      throw new Error(`Distribution member changed while reading it: ${relative}`);
    }
    assertUnlinkedPath(filename, "Distribution member");
    return { contents, mode: before.mode & 0o111 ? 0o755 : 0o644 };
  } finally { closeSync(descriptor); }
}

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

function writePayloadFile(filename, contents, mode) {
  const descriptor = openSync(filename, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0), 0o600);
  try {
    writeFileSync(descriptor, contents);
    // Do not let the bootstrap's private umask change pinned member modes.
    fchmodSync(descriptor, mode);
  } finally { closeSync(descriptor); }
}

/** Windows environment names are case-insensitive, including plain test objects. */
export function normalizePayloadEnvironment(environment = process.env, platform = process.platform) {
  const result = {};
  const seen = new Map();
  for (const [key, value] of Object.entries(environment)) {
    if (value === undefined) continue;
    const identity = platform === "win32" ? key.toUpperCase() : key;
    if (seen.has(identity)) {
      if (seen.get(identity) !== value) throw new Error(`Conflicting case-insensitive Windows environment values for ${key}.`);
      continue;
    }
    seen.set(identity, value);
    result[platform === "win32" ? identity : key] = value;
  }
  return result;
}

function environmentValue(environment, name) {
  return environment[process.platform === "win32" ? name.toUpperCase() : name];
}

function npmEnvironment(environment) {
  const child = {};
  const forbidden = new Set([
    "NODE_OPTIONS", "NODE_PATH", "BASH_ENV", "ENV", "NPM_CONFIG_PREFIX", "NPM_CONFIG_GLOBAL",
    "NPM_CONFIG_WORKSPACE", "NPM_CONFIG_WORKSPACES", "NPM_CONFIG_INCLUDE_WORKSPACE_ROOT",
  ]);
  for (const [key, value] of Object.entries(environment)) {
    if (value !== undefined && !forbidden.has(key.toUpperCase())) child[key] = value;
  }
  return child;
}

function explicitNpmConfig(source, environment, name) {
  const values = [...new Set(Object.entries(environment)
    .filter(([key]) => key.toUpperCase() === name).map(([, value]) => value))];
  if (values.length > 1) throw new Error(`Conflicting environment aliases for ${name}.`);
  if (!values[0]) return Buffer.alloc(0);
  // Match npm's path-valued configuration: trim, environment substitution,
  // then home expansion. A literal ~/ path must not become an empty snapshot.
  const selected = values[0].trim().replace(/(?<!\\)(\\*)\$\{([^${}?]+)(\?)?\}/gu, (match, escapes, variable, optional) => {
    if (escapes.length % 2) return match.slice((escapes.length + 1) / 2);
    return escapes.slice(escapes.length / 2) + (environmentValue(environment, variable) ?? (optional ? "" : `\${${variable}}`));
  });
  if (!selected || selected === os.devNull) return Buffer.alloc(0);
  const home = environmentValue(environment, "HOME") || os.homedir();
  const filename = (process.platform === "win32" ? /^~[/\\]/u : /^~\//u).test(selected)
    ? path.resolve(source, home, selected.slice(2)) : path.resolve(source, selected);
  if (!inspect(filename)) return Buffer.alloc(0); // npm permits a missing config.
  const resolved = realpathSync(filename);
  const metadata = lstatSync(resolved);
  if (!metadata.isFile() || metadata.size > 1024 * 1024) {
    throw new Error(`Explicit npm configuration must be a bounded regular file: ${filename}`);
  }
  return regularFileBytes(path.dirname(resolved), path.basename(resolved), 1024 * 1024).contents;
}

/** Keep pack's writes out of the user's cache, including in a dry run. */
function withNpmInspection(source, outputRoot, environment, run) {
  // Preserve explicitly selected policy/auth settings without loading default
  // user/global config. These private snapshots never enter the payload.
  const configs = [explicitNpmConfig(source, environment, "NPM_CONFIG_USERCONFIG"),
    explicitNpmConfig(source, environment, "NPM_CONFIG_GLOBALCONFIG")];
  const created = [];
  let scratch;
  let scratchIdentity;
  let failure;
  const sameDirectory = (directory, expected) => {
    assertUnlinkedPath(directory, "npm inspection storage");
    const actual = inspect(directory);
    if (!actual?.isDirectory() || actual.dev !== expected.dev || actual.ino !== expected.ino) {
      throw new Error(`npm inspection directory changed identity: ${directory}`);
    }
  };
  try {
    const missing = [];
    let parent = outputRoot;
    while (!inspect(parent)) { missing.push(parent); parent = path.dirname(parent); }
    assertUnlinkedPath(parent, "npm inspection parent");
    if (!lstatSync(parent).isDirectory()) throw new Error(`npm inspection parent is not a directory: ${parent}`);
    for (const directory of missing.reverse()) {
      assertUnlinkedPath(path.dirname(directory), "npm inspection parent");
      mkdirSync(directory, { mode: 0o700 });
      created.push([directory, lstatSync(directory)]);
    }
    assertUnlinkedPath(outputRoot, "npm inspection output");
    scratch = mkdtempSync(path.join(outputRoot, ".npm-inspect-"));
    scratchIdentity = lstatSync(scratch);
    const cache = path.join(scratch, "cache");
    const logs = path.join(scratch, "logs");
    const temporary = path.join(scratch, "tmp");
    for (const directory of [cache, logs, temporary]) mkdirSync(directory, { mode: 0o700 });
    const userConfig = path.join(scratch, "user.npmrc");
    const globalConfig = path.join(scratch, "global.npmrc");
    writePayloadFile(userConfig, configs[0], 0o600);
    writePayloadFile(globalConfig, configs[1], 0o600);
    const env = npmEnvironment(environment);
    for (const key of Object.keys(env)) {
      if (["NPM_CONFIG_CACHE", "NPM_CONFIG_LOGS_DIR", "NPM_CONFIG_USERCONFIG", "NPM_CONFIG_GLOBALCONFIG", "TMP", "TEMP", "TMPDIR"].includes(key.toUpperCase())) delete env[key];
    }
    Object.assign(env, { TMP: temporary, TEMP: temporary, TMPDIR: temporary,
      npm_config_cache: cache, npm_config_logs_dir: logs,
      npm_config_userconfig: userConfig, npm_config_globalconfig: globalConfig });
    // CLI precedence also fences redirects in the source project's .npmrc.
    return run(env, ["--cache", cache, "--logs-dir", logs, "--userconfig", userConfig,
      "--globalconfig", globalConfig, "--prefix", source, "--global=false"]);
  } catch (error) {
    failure = error;
    throw error;
  } finally {
    try {
      if (scratch) {
        sameDirectory(scratch, scratchIdentity);
        rmSync(scratch, { recursive: true });
      }
      for (const [directory, identity] of created.reverse()) {
        sameDirectory(directory, identity);
        try { rmdirSync(directory); }
        catch (error) {
          // Another preparation may now use a parent we created. Never remove
          // its contents, or any pre-existing output directory.
          if (error.code !== "ENOTEMPTY" && error.code !== "EEXIST") throw error;
        }
      }
    } catch (cleanupError) {
      if (failure) throw new AggregateError([failure, cleanupError], `${failure.message}; npm inspection cleanup failed: ${cleanupError.message}`);
      throw cleanupError;
    }
  }
}

/** Invoke npm's installed JavaScript entrypoint directly, including on Windows. */
function managedExecutableRoots(sourceRoot, runtimeRoot, toolchainRoot, selectedToolchainRoot, environment) {
  const roots = new Set([sourceRoot, runtimeRoot, toolchainRoot, selectedToolchainRoot]);
  for (const selected of [...roots]) {
    const local = path.join(selected, ".local");
    if (existsSync(local)) roots.add(path.dirname(realpathSync(local)));
  }
  for (const name of ["GB_STUDIO_WORKSPACE_ROOT", "GB_STUDIO_PROJECT_ROOT"]) {
    const configured = environmentValue(environment, name)?.trim();
    if (!configured) continue;
    const selected = path.resolve(configured);
    try { roots.add(statSync(selected).isFile() ? path.dirname(realpathSync(selected)) : realpathSync(selected)); }
    catch { roots.add(selected); }
  }
  const current = realpathSync(process.cwd());
  // Running from one's home or a drive root must not ban an ordinary NVM/global
  // installation. Explicit source/runtime/toolchain/project roots still apply.
  if (canonicalKey(current) !== canonicalKey(os.homedir()) && current !== path.parse(current).root) roots.add(current);
  return [...roots];
}

function resolveNpmCli(rejectedRoots, environment) {
  const nodeDirectory = path.dirname(realpathSync(process.execPath));
  const candidates = [
    path.join(nodeDirectory, "node_modules", "npm", "bin", "npm-cli.js"),
    path.resolve(nodeDirectory, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
  ];
  const configured = environmentValue(environment, "npm_execpath");
  if (configured && path.isAbsolute(configured)) candidates.push(configured);
  for (const entry of (environmentValue(environment, "PATH") ?? "").split(path.delimiter)) {
    const directory = entry.trim().replace(/^"(.*)"$/u, "$1");
    if (!directory || !path.isAbsolute(directory)) continue;
    candidates.push(path.join(directory, "node_modules", "npm", "bin", "npm-cli.js"));
    candidates.push(path.join(directory, "npm"));
  }
  for (const candidate of candidates) {
    try {
      const resolved = realpathSync(candidate);
      if (rejectedRoots.some((root) => isWithin(root, resolved)) || path.basename(resolved) !== "npm-cli.js" || !statSync(resolved).isFile()) continue;
      if (readObject(path.resolve(path.dirname(resolved), "..", "package.json"), "npm manifest").name !== "npm") continue;
      return resolved;
    } catch { /* Try another installed npm location. */ }
  }
  throw new Error("Could not locate npm's installed npm-cli.js beside trusted Node or on an absolute PATH outside the selected checkout.");
}

// Follow npm pack's directory inventory without constructing its throwaway
// level-9 gzip tarball. Resolve each dependency from its actual consumer so
// nested npm dependency layouts use the same versions as libnpmpack/Pacote.
// This runs in a child with the existing isolated npm environment and bounds.
const NPM_INVENTORY_PROGRAM = String.raw`
const fs = require("node:fs");
const path = require("node:path");
const { createRequire } = require("node:module");
const [npmCli, source, rejectedRootsJson, ...storageArgs] = process.argv.slice(1);
const npmRoot = fs.realpathSync(path.dirname(path.dirname(npmCli)));
const rejectedRoots = JSON.parse(rejectedRootsJson);
const within = (root, candidate) => {
  const relative = path.relative(root, candidate);
  return relative === "" || relative !== ".." && !relative.startsWith(".." + path.sep) && !path.isAbsolute(relative);
};
const unsupported = () => { throw Object.assign(new Error("Unsupported npm inventory layout or API"), { code: "NPM_INVENTORY_UNSUPPORTED" }); };
let supportedLayout = true;
const resolve = (consumer, name) => {
  let entry;
  try { entry = fs.realpathSync(createRequire(consumer).resolve(name)); }
  catch (error) {
    if (["MODULE_NOT_FOUND", "ERR_PACKAGE_PATH_NOT_EXPORTED", "ENOENT"].includes(error.code)) return undefined;
    throw error;
  }
  if (rejectedRoots.some(root => within(root, entry))) throw new Error("npm inventory dependency resolves inside a managed project: " + name);
  // Distro-split layouts can still use the established trusted npm CLI. Never
  // directly load their external modules in this optimization's fast path.
  if (!within(npmRoot, entry) || !fs.statSync(entry).isFile()) supportedLayout = false;
  return entry;
};
const consumer = (relative) => {
  const expected = path.join(npmRoot, relative);
  let entry;
  try { entry = fs.realpathSync(expected); }
  catch (error) {
    if (error.code !== "ENOENT") throw error;
    supportedLayout = false;
    return expected;
  }
  if (rejectedRoots.some(root => within(root, entry))) throw new Error("npm inventory consumer resolves inside a managed project: " + relative);
  if (!within(npmRoot, entry) || !fs.statSync(entry).isFile()) supportedLayout = false;
  return entry;
};
async function inventory() {
  // Match the installed CLI's consumers, including lib/node_modules overrides.
  // Missing or external consumer files disable the optimization, but we still
  // check the known dependency chain before invoking the trusted CLI fallback.
  const packConsumer = consumer("lib/commands/pack.js");
  const configConsumer = consumer("lib/npm.js");
  const libpack = resolve(packConsumer, "libnpmpack");
  const arborist = libpack && resolve(libpack, "@npmcli/arborist");
  const pacote = libpack && resolve(libpack, "pacote");
  const packlistEntry = pacote && resolve(pacote, "npm-packlist");
  const configEntry = resolve(configConsumer, "@npmcli/config");
  const definitionsEntry = resolve(configConsumer, "@npmcli/config/lib/definitions");
  // Complete every resolvable entrypoint's trust check before selecting the
  // compatibility fallback. A safe split dependency cannot hide a later
  // dependency redirected into the checkout.
  if (!supportedLayout || ![libpack, arborist, pacote, packlistEntry, configEntry, definitionsEntry].every(Boolean)) unsupported();
  const Arborist = require(arborist);
  const packlist = require(packlistEntry);
  const Config = require(configEntry);
  const { definitions, flatten, nerfDarts, shorthands } = require(definitionsEntry);
  if (typeof Arborist !== "function" || typeof Arborist.prototype?.loadActual !== "function" || typeof packlist !== "function"
      || typeof Config !== "function" || typeof Config.prototype?.load !== "function" || typeof Config.prototype?.validate !== "function"
      || typeof Config.prototype?.get !== "function"
      || !definitions || typeof flatten !== "function" || !shorthands) unsupported();
  // Match npm's config load/validation before file selection. The explicit CLI
  // options keep workspace selection, lifecycle scripts, and storage fenced.
  const config = new Config({ npmPath: npmRoot, definitions, flatten, nerfDarts, shorthands,
    argv: [process.execPath, npmCli, "pack", "--dry-run", "--json", "--ignore-scripts", "--workspaces=false", ...storageArgs] });
  await config.load();
  config.validate();
  const workspaces = config.get("workspaces");
  const workspace = config.get("workspace");
  if ((workspaces !== null && typeof workspaces !== "boolean") || !Array.isArray(workspace)) unsupported();
  if (workspaces === false && workspace.length) {
    throw new Error("Cannot use --no-workspaces and --workspace at the same time");
  }
  const tree = await new Arborist({ path: source }).loadActual();
  if (!tree.package._id) throw new Error("Invalid package, must have name and version");
  // The launcher fixes prefix to source and disables workspace selection.
  // Omit workspaces, as npm pack does for --workspaces=false.
  const files = await packlist(tree, { path: source, prefix: source });
  process.stdout.write(JSON.stringify([{ name: tree.package.name, files: files.map(path => ({ path })) }]));
}
inventory().catch(error => {
  if (error.code === "NPM_INVENTORY_UNSUPPORTED") {
    process.stdout.write("NPM_INVENTORY_UNSUPPORTED\n"); process.exitCode = 78;
  } else { process.stderr.write(error.message + "\n"); process.exitCode = 1; }
});
`;

function npmDistributionFiles(source, rejectedRoots, environment, outputRoot) {
  const npmCli = resolveNpmCli(rejectedRoots, environment);
  const result = withNpmInspection(source, outputRoot, environment, (env, storageArgs) => {
    const started = performance.now();
    let execution = spawnSync(realpathSync(process.execPath), [
      "--input-type=commonjs", "--eval", NPM_INVENTORY_PROGRAM, npmCli, source, JSON.stringify(rejectedRoots), ...storageArgs,
    ], {
      cwd: source, env, shell: false, windowsHide: true,
      encoding: "utf8", timeout: 60_000, maxBuffer: 4 * 1024 * 1024,
    });
    if (!execution.error && execution.status === 78 && execution.stdout === "NPM_INVENTORY_UNSUPPORTED\n") {
      const remaining = 60_000 - Math.ceil(performance.now() - started);
      if (remaining <= 0) throw new Error("npm package inspection exceeded its 60-second deadline.");
      execution = spawnSync(realpathSync(process.execPath), [
        npmCli, "pack", "--dry-run", "--json", "--ignore-scripts", "--workspaces=false", ...storageArgs,
      ], { cwd: source, env, shell: false, windowsHide: true,
        encoding: "utf8", timeout: remaining, maxBuffer: 4 * 1024 * 1024 });
    }
    if (execution.error || execution.status !== 0) {
      throw new Error(`npm could not inspect the distributable package: ${execution.error?.message ?? execution.stderr?.trim() ?? "unknown failure"}`);
    }
    return execution;
  });
  let packages;
  try { packages = JSON.parse(result.stdout); }
  catch (cause) {
    throw new Error(`npm package inspection returned invalid JSON for ${source}.`, { cause });
  }
  if (!Array.isArray(packages) || packages.length !== 1 || packages[0]?.name !== PLUGIN_NAME || !Array.isArray(packages[0]?.files)) {
    throw new Error("npm pack did not report exactly one modretro-chromatic package.");
  }
  const files = packages[0].files.map((file) => file.path);
  if (files.length > MAX_FILES - 3 || new Set(files).size !== files.length) throw new Error("The distribution file list is oversized or contains duplicate paths.");
  for (const filename of files) {
    if (isForbiddenPayloadPath(filename)) throw new Error(`Refusing forbidden distribution member: ${filename}`);
  }
  return files.sort();
}

function preparedRuntime(candidate) {
  const runtimeRoot = realDirectory(candidate, "Prepared runtime");
  const required = ["dist/server.js", "scripts/emulator_worker.py"];
  for (const filename of required) {
    const selected = path.join(runtimeRoot, filename);
    if (!statSync(selected).isFile() || !isWithin(runtimeRoot, realpathSync(selected))) {
      throw new Error(`Prepared runtime is incomplete or redirected: ${selected}`);
    }
  }
  const sdk = realDirectory(path.join(runtimeRoot, "node_modules", "@modelcontextprotocol", "sdk"), "Installed MCP SDK");
  if (!isWithin(runtimeRoot, sdk)) throw new Error("The installed MCP SDK must belong to the selected prepared runtime.");
  return runtimeRoot;
}

function resolveToolchainRoot(candidate) {
  const selected = realDirectory(candidate, "Toolchain root");
  const local = path.join(selected, ".local");
  if (!existsSync(local)) return selected;
  const resolved = realDirectory(local, "Existing local toolchain");
  if (path.basename(resolved).toLowerCase() !== ".local") throw new Error("The existing toolchain must resolve to a real .local directory.");
  return path.dirname(resolved);
}

function isRuntimeFile(filename) {
  return filename.startsWith("dist/") || filename.startsWith("assets/devices/") || filename === "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(filename) ||
    filename.startsWith("third-party/chromatic-cli/") ||
    (filename.startsWith("native/capture/macos/") || filename.startsWith("native/capture/linux/")) || filename.startsWith("native-game-plugin/") || filename.startsWith("examples/");
}

function assertExactRuntimeSubtree(runtimeRoot, files, subtree = "dist") {
  const expectedFiles = new Set(files.filter((file) => file.path.startsWith(`${subtree}/`)).map((file) => file.path));
  const expectedDirectories = new Set([subtree]);
  for (const filename of expectedFiles) {
    const components = filename.split("/");
    for (let index = 1; index < components.length; index++) expectedDirectories.add(components.slice(0, index).join("/"));
  }
  const found = new Set();
  const repair = subtree === "dist" ? "Clean its generated dist directory and run npm run build" : subtree.startsWith("native/capture/") ? "Restore the pinned native capture runtime" : "Restore the intact bundled Wrecklight sample";
  const reject = (relative) => {
    throw new Error(`The selected runtime ${subtree} has an unlisted or redirected entry: ${relative}. ${repair} before preparing the plugin again.`);
  };
  function visit(relative) {
    const directory = path.join(runtimeRoot, ...relative.split("/"));
    assertUnlinkedPath(directory, "Prepared runtime dist");
    for (const entry of readdirSync(directory, { withFileTypes: true })) {
      const child = `${relative}/${entry.name}`;
      if (entry.isSymbolicLink()) reject(child);
      if (entry.isDirectory()) {
        if (!expectedDirectories.has(child)) reject(child);
        visit(child);
      } else {
        if (!entry.isFile() || !expectedFiles.has(child)) reject(child);
        found.add(child);
      }
    }
  }
  visit(subtree);
  if (found.size !== expectedFiles.size) {
    throw new Error(`The selected runtime ${subtree} is incomplete. ${repair} before preparing the plugin again.`);
  }
}

function assertRuntimeMatchesSource(sourceRoot, runtimeRoot, packageJson, files) {
  if (canonicalKey(sourceRoot) === canonicalKey(runtimeRoot)) return;
  const runtimePackage = readObject(path.join(runtimeRoot, "package.json"), "Runtime package manifest");
  if (runtimePackage.name !== packageJson.name || runtimePackage.version !== packageJson.version) {
    throw new Error("The selected runtime package identity/release does not match the distributable source; prepare that exact release first.");
  }
  const target = chromaticTargetFromManifest(packageJson);
  const format = chromaticFormatFromManifest(packageJson);
  if (chromaticTargetFromManifest(runtimePackage) !== target) {
    throw new Error("The selected runtime platform does not match the distributable source.");
  }
  if (chromaticFormatFromManifest(runtimePackage) !== format) throw new Error("Runtime and package Chromatic formats differ.");
  assertExactRuntimeSubtree(runtimeRoot, files);
  for (const subtree of ["native/capture/macos", "native/capture/linux"]) {
    if (files.some((file) => file.path.startsWith(`${subtree}/`))) assertExactRuntimeSubtree(runtimeRoot, files, subtree);
  }
  if (files.some((file) => file.path.startsWith("examples/wrecklight/"))) assertExactRuntimeSubtree(runtimeRoot, files, "examples/wrecklight");
  if (packageJson.bundledChromatic === CHROMATIC_VERSION) verifyChromaticBundle(runtimeRoot, { target, format });
  for (const file of files) {
    if (!isRuntimeFile(file.path)) continue;
    const actual = regularFileBytes(runtimeRoot, file.path);
    if (digest(actual.contents) !== file.sha256 || ((file.path.startsWith("native/capture/macos/") || file.path.startsWith("native/capture/linux/")) && actual.mode !== file.mode)) {
      throw new Error(`The selected runtime does not match the distributable source: ${file.path}. Build the selected release before registering it.`);
    }
  }
}

function buildPlan(options) {
  const portable = options.portable === true;
  const selfContained = options.selfContained === true;
  if (selfContained && !portable) throw new Error("--self-contained requires --portable.");
  const compactRequested = options.compact === true;
  if (options.compact !== undefined && typeof options.compact !== "boolean") throw new Error("compact must be a boolean.");
  if (compactRequested && !portable) throw new Error("--compact requires --portable.");
  if (options.portable !== undefined && typeof options.portable !== "boolean") throw new Error("portable must be a boolean.");
  if (options.zip !== undefined && typeof options.zip !== "boolean") throw new Error("zip must be a boolean.");
  if (options.zip && !portable) throw new Error("--zip requires --portable; machine-local payloads cannot be uploaded.");
  if (options.target !== undefined && !portable) throw new Error("--target is only available with --portable; local payloads retain their source platform.");
  if (portable && (options.runtimeRoot !== undefined || options.toolchainRoot !== undefined)) {
    throw new Error("A portable distribution cannot bind a local runtime or toolchain.");
  }
  if (portable && process.platform === "win32") {
    throw new Error("Portable distributions must be prepared on a POSIX host to preserve pinned file modes.");
  }
  const environment = normalizePayloadEnvironment(options.environment ?? process.env);
  const sourceRoot = realDirectory(options.source ?? SCRIPT_ROOT, "Plugin source");
  const packageJson = readObject(path.join(sourceRoot, "package.json"), "Package manifest");
  const pluginJson = readObject(path.join(sourceRoot, ".codex-plugin", "plugin.json"), "Plugin manifest");
  if (packageJson.name !== PLUGIN_NAME || pluginJson.name !== PLUGIN_NAME || typeof pluginJson.version !== "string") {
    throw new Error(`The selected source must declare a versioned ${PLUGIN_NAME} plugin.`);
  }
  if (pluginJson.version.split("+")[0] !== packageJson.version) throw new Error("Package and plugin release versions differ; finish the release metadata before staging.");
  if (!Array.isArray(packageJson.files) || packageJson.files.length === 0) throw new Error("The selected package needs an explicit reviewed npm files allowlist.");
  const sourceTarget = chromaticTargetFromManifest(packageJson);
  const sourceFormat = chromaticFormatFromManifest(packageJson);
  const target = options.target === undefined ? sourceTarget
    : chromaticTargetFromManifest({ ...packageJson, bundledChromaticTarget: options.target });
  if (sourceTarget !== undefined && sourceTarget !== target) throw new Error("A platform distribution cannot be retargeted to another platform.");
  // Keep the established package:upload --target shortcut. Explicit platform
  // selection retains the raw targeted format instead of shipping all six.
  const compact = compactRequested && target === undefined;
  const format = compact ? sourceFormat ?? CHROMATIC_PACKED_V2.format : sourceFormat;
  if (format !== undefined && target !== undefined) throw new Error("Packed distributions are universal; use the raw source for an explicit --target package.");
  if (!portable) requireChromaticHostTarget(target);
  const distributionPackage = { ...packageJson, ...(selfContained ? { bundledMcp: true } : {}), ...(target === undefined ? {} : { bundledChromaticTarget: target }),
    ...(format === undefined ? {} : { bundledChromaticFormat: format }) };
  const runtimeRoot = portable ? sourceRoot : preparedRuntime(options.runtimeRoot ?? sourceRoot);
  const selectedToolchainRoot = portable ? sourceRoot
    : realDirectory(options.toolchainRoot ?? environmentValue(environment, "GB_STUDIO_TOOLCHAIN_ROOT") ?? runtimeRoot, "Selected toolchain root");
  const toolchainRoot = portable ? sourceRoot : resolveToolchainRoot(selectedToolchainRoot);
  const nodeExecutable = realpathSync(process.execPath);
  const rejectedExecutableRoots = managedExecutableRoots(sourceRoot, runtimeRoot, toolchainRoot, selectedToolchainRoot, environment);
  if (rejectedExecutableRoots.some((root) => isWithin(root, nodeExecutable))) {
    throw new Error("The Node executable must be installed outside the selected source, runtime, toolchain, and project directories.");
  }
  const outputRoot = assertUnlinkedPath(options.outputRoot ?? path.join(sourceRoot, "artifacts", portable ? "plugin-distributions" : "plugin-payloads"), "Payload output root");
  if (isWithin(sourceRoot, outputRoot) && !isWithin(path.join(sourceRoot, "artifacts"), outputRoot)) {
    throw new Error("A payload output root inside the checkout must be beneath its ignored artifacts directory.");
  }
  for (const root of new Set([sourceRoot, runtimeRoot, toolchainRoot])) {
    for (const directory of [".local", "node_modules", "vendor"]) {
      if (isWithin(path.join(root, directory), outputRoot)) throw new Error("Payload output must not be inside a runtime dependency or toolchain directory.");
    }
  }
  const files = [];
  let totalBytes = 0;
  const standalone = selfContained ? bundleMcp(sourceRoot) : undefined;
  const sourceFiles = npmDistributionFiles(sourceRoot, rejectedExecutableRoots, environment, outputRoot);
  if (packageJson.bundledChromatic === CHROMATIC_VERSION) {
    for (const required of chromaticRequiredPaths(sourceTarget, sourceFormat)) {
      if (!sourceFiles.includes(required)) throw new Error(`The distribution is missing bundled Chromatic member: ${required}`);
    }
    // Authenticate the complete source bundle before selecting any platform.
    verifyChromaticBundle(sourceRoot, { target: sourceTarget, format: sourceFormat });
  }
  const packing = new Map(compact && sourceFormat === undefined ? [
    [CHROMATIC_PACKED_V2.source, `${CHROMATIC_ROOT}/${CHROMATIC_PACKED_V2.member}`],
    [CHROMATIC_NOTICE_PACK.source, `${CHROMATIC_ROOT}/${CHROMATIC_NOTICE_PACK.member}`],
    [CHROMATIC_ARTWORK_PACK.source, `assets/devices/${CHROMATIC_ARTWORK_PACK.member}`],
  ] : []);
  for (const source of packing.keys()) if (!sourceFiles.includes(source)) {
    throw new Error(`The compact distribution requires its pinned source stream in the package allowlist: ${source}`);
  }
  const selectedVendorPaths = new Set(chromaticRequiredPaths(target, format));
  for (const filename of sourceFiles) {
    if (compact && (filename.startsWith("examples/wrecklight/") || (filename.startsWith("dist/") && filename.endsWith(".d.ts")))) continue;
    if (packing.size && filename.startsWith(CHROMATIC_ROOT + "/") && !selectedVendorPaths.has(filename)) continue;
    if (packing.size && filename.startsWith("assets/devices/") && Object.hasOwn(CHROMATIC_ARTWORK_MEMBERS, filename.slice("assets/devices/".length))) {
      const source = regularFileBytes(sourceRoot, filename), pin = CHROMATIC_ARTWORK_MEMBERS[filename.slice("assets/devices/".length)];
      if (source.contents.length !== pin.size || digest(source.contents) !== pin.sha256 || source.mode !== pin.mode) {
        throw new Error(`Device artwork differs from its packed source pin: ${filename}`);
      }
      continue;
    }
    if (CHROMATIC_STORAGE_SOURCES.includes(filename) && !packing.has(filename)) continue;
    const destinationName = packing.get(filename) ?? filename;
    if (portable && target !== undefined && filename.startsWith(CHROMATIC_ROOT + "/") && !selectedVendorPaths.has(filename)) continue;
    let { contents, mode } = regularFileBytes(sourceRoot, filename);
    if (portable && filename.startsWith(CHROMATIC_ROOT + "/")) {
      // Publish the pinned portable mode, not a foreign host's normalized mode.
      const member = [CHROMATIC_PACKED, CHROMATIC_PACKED_V2, CHROMATIC_NOTICE_PACK]
        .find((pack) => filename === `${CHROMATIC_ROOT}/${pack.member}`) ?? CHROMATIC_MEMBERS[filename.slice(CHROMATIC_ROOT.length + 1)];
      if (!member) throw new Error(`Unrecognized bundled Chromatic member: ${filename}`);
      mode = member.mode;
    }
    if (filename === ".codex-plugin/plugin.json") {
      // Canonicalize before deriving the generated cache version so the
      // launcher can reconstruct and verify that identity from the payload.
      contents = Buffer.from(`${JSON.stringify(pluginJson, null, 2)}\n`);
      mode = 0o644;
    }
    if (filename === ".mcp.json") {
      contents = Buffer.from(`${JSON.stringify({ mcpServers: { "modretro-chromatic": {
        // Keep installed tools alive through the bounded compiler and validation.
        tool_timeout_sec: 900,
        type: "stdio", cwd: ".", command: portable ? "node" : nodeExecutable, args: ["./scripts/start-mcp.mjs"],
        env: portable ? { GB_STUDIO_LOG_LEVEL: "info" }
          : { GB_STUDIO_LOG_LEVEL: "info", GB_STUDIO_RUNTIME_ROOT: runtimeRoot, GB_STUDIO_TOOLCHAIN_ROOT: toolchainRoot },
      } } }, null, 2)}\n`);
      mode = 0o644;
    }
    if (filename === "package.json") {
      // Local update payloads must not be repacked with absolute launch paths.
      // Portable distributions retain the reviewed package allowlist instead.
      contents = Buffer.from(`${JSON.stringify(portable ? distributionPackage
        : { ...distributionPackage, private: true, files: [], scripts: {} }, null, 2)}\n`);
      mode = 0o644;
    }
    if (standalone && filename === "dist/server.js") {
      const sourceVersion = contents.toString("utf8").match(/\b(?:const|var)\s+SERVER_VERSION\s*=\s*["']([^"']+)["']/u)?.[1];
      if (sourceVersion !== packageJson.version) throw new Error("The compiled MCP server version is stale; run npm run build first.");
      contents = standalone.contents;
    }
    if (standalone && filename === "scripts/device-client-deps.mjs") contents = standalone.clientContents;
    if (contents.length > MAX_FILE_BYTES) throw new Error(`Distribution member exceeds byte limit: ${filename}`);
    totalBytes += contents.length;
    if (totalBytes > MAX_TOTAL_BYTES) throw new Error(`The distributable payload exceeds ${MAX_TOTAL_BYTES} bytes.`);
    files.push({ path: destinationName, contents, mode, size: contents.length, sha256: digest(contents) });
  }
  if (standalone) {
    const contents = standalone.notices;
    if (contents.length > MAX_FILE_BYTES || totalBytes + contents.length > MAX_TOTAL_BYTES) {
      throw new Error("Bundled dependency notices exceed distribution byte limits.");
    }
    files.push({ path: "licenses/bundled-javascript.txt", contents, mode: 0o644, size: contents.length, sha256: digest(contents) });
    totalBytes += contents.length;
  }
  for (const filename of portable ? [] : [".gitignore", ".npmignore"]) {
    const contents = Buffer.from("*\n");
    files.push({ path: filename, contents, mode: 0o644, size: contents.length, sha256: digest(contents) });
    totalBytes += contents.length;
  }
  files.sort((left, right) => left.path.localeCompare(right.path, "en"));
  if (packageJson.bundledChromatic !== undefined) {
    if (packageJson.bundledChromatic !== CHROMATIC_VERSION) throw new Error("Unsupported bundled Chromatic release.");
    for (const required of chromaticRequiredPaths(target, format)) {
      if (!files.some((file) => file.path === required)) throw new Error(`The distribution is missing bundled Chromatic member: ${required}`);
    }
    verifyChromaticRecords(files, { target, format });
  }
  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 (!files.some((file) => file.path === required)) throw new Error(`The npm distribution is missing ${required}; run npm run build first.`);
  }
  if (options.zip) assertUploadIcons(pluginJson, files);
  const serverVersion = files.find((file) => file.path === "dist/server.js").contents.toString("utf8")
    .match(/\b(?:const|var)\s+SERVER_VERSION\s*=\s*["']([^"']+)["']/u)?.[1];
  if (serverVersion !== packageJson.version) throw new Error("The compiled MCP server version is stale or unrecognized; run npm run build in the selected source before staging.");
  if (!portable) assertRuntimeMatchesSource(sourceRoot, runtimeRoot, distributionPackage, files);
  const inventoryOf = () => files.map(({ path: filename, mode, size, sha256 }) => ({ path: filename, mode, size, sha256 }));
  // Codex caches by manifest version. Bind that version to the machine-local
  // launcher as well as the source bytes, without mutating the shipped release.
  const sourceVersion = pluginJson.version;
  const contentIdentity = digest(JSON.stringify(inventoryOf()));
  const version = `${sourceVersion.split("+")[0]}+codex.${portable ? "distribution" : "payload"}.${contentIdentity.slice(0, 24)}`;
  const manifestFile = files.find((file) => file.path === ".codex-plugin/plugin.json");
  const rewrittenManifest = Buffer.from(`${JSON.stringify({ ...pluginJson, version }, null, 2)}\n`);
  totalBytes += rewrittenManifest.length - manifestFile.size;
  Object.assign(manifestFile, { contents: rewrittenManifest, size: rewrittenManifest.length, sha256: digest(rewrittenManifest) });
  const inventory = inventoryOf();
  const payloadId = digest(JSON.stringify(inventory));
  const payloadPath = path.join(outputRoot, payloadId.slice(0, 32), PLUGIN_NAME);
  const runtime = { packageName: packageJson.name, version: packageJson.version, files: inventory.filter((file) => isRuntimeFile(file.path)) };
  const receipt = Buffer.from(`${JSON.stringify(portable ? {
    schemaVersion: 1, kind: "portable-distribution", pluginName: PLUGIN_NAME, sourceVersion, version,
    distributionId: payloadId, target: target ?? "universal", files: inventory,
  } : {
    schemaVersion: 1, pluginName: PLUGIN_NAME, sourceVersion, version, payloadId,
    runtimeRoot, toolchainRoot, nodeExecutable, runtime, files: inventory,
  }, null, 2)}\n`);
  if (receipt.length > 1024 * 1024 || totalBytes + receipt.length > MAX_TOTAL_BYTES) {
    throw new Error("The complete local payload and its receipt exceed the bounded distribution limits.");
  }
  return { sourceRoot, outputRoot, payloadPath, payloadId, runtimeRoot, toolchainRoot, nodeExecutable,
    sourceVersion, version, files, receipt, totalBytes: totalBytes + receipt.length, portable, target };
}

function assertUploadIcons(manifest, files) {
  if (files.some((file) => file.path === DISTRIBUTION_MANIFEST)) throw new Error("distribution.json belongs outside the uploaded plugin.");
  // Only the maintainer ZIP command needs the existing image dependency.
  const { PNG } = createRequire(import.meta.url)("pngjs");
  for (const field of ["composerIcon", "logo"]) {
    const reference = manifest.interface?.[field];
    const relative = typeof reference === "string" && reference.startsWith("./") ? reference.slice(2) : "";
    const icon = !isForbiddenPayloadPath(relative) && files.find((file) => file.path === relative);
    if (!icon) throw new Error(`Upload requires interface.${field} to reference a packaged local PNG.`);
    const png = icon.contents;
    if (png.length < 33 || !png.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])) ||
        png.readUInt32BE(8) !== 13 || png.toString("ascii", 12, 16) !== "IHDR" ||
        png.readUInt32BE(16) === 0 || png.readUInt32BE(16) > 4096 || png.readUInt32BE(16) !== png.readUInt32BE(20)) {
      throw new Error(`Upload requires a square PNG no larger than 4096px for interface.${field}: ${reference}`);
    }
    try { PNG.sync.read(png, { checkCRC: true }); }
    catch (cause) { throw new Error(`Upload requires a complete, decodable PNG for interface.${field}: ${reference}`, { cause }); }
  }
}

// Parent-first, stable directory inventory shared by staging and ZIP publication.
function payloadDirectories(files) {
  const directories = new Set();
  for (const file of files) {
    const parts = file.path.split("/");
    for (let i = 1; i < parts.length; i++) directories.add(parts.slice(0, i).join("/"));
  }
  return [...directories].sort();
}

function createPayloadDirectory(directory) {
  mkdirSync(directory, { mode: 0o755 });
  // Only fresh directories inside our private staging tree are normalized.
  if (process.platform !== "win32") chmodSync(directory, 0o755);
}

/** ZIP32 is sufficient for the bounded payload. Exclude its outer container. */
function writeUploadZip(plan, zipPath) {
  const stagingRoot = mkdtempSync(path.join(plan.outputRoot, ".upload-"));
  const stagedZip = path.join(stagingRoot, "plugin.zip");
  let descriptor;
  try {
    descriptor = openSync(stagedZip, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 0o600);
    let offset = 0;
    const hash = createHash("sha256");
    const central = [];
    const append = (bytes) => { writeFileSync(descriptor, bytes); hash.update(bytes); offset += bytes.length; };
    const entries = [
      ...payloadDirectories(plan.files).map((directory) => ({
        path: directory + "/", contents: Buffer.alloc(0), size: 0, mode: 0o755, directory: true,
      })),
      ...plan.files,
    ];
    for (const file of entries) {
      const name = Buffer.from(file.path, "utf8");
      const deflated = deflateRawSync(file.contents, { level: 9 });
      const method = deflated.length < file.contents.length ? 8 : 0;
      const compressed = method === 8 ? deflated : file.contents;
      const crc = setupCrc32(file.contents);
      const local = Buffer.alloc(30);
      local.writeUInt32LE(0x04034b50, 0);
      local.writeUInt16LE(20, 4);
      local.writeUInt16LE(0x0800, 6); // UTF-8 names; no encryption or data descriptors.
      local.writeUInt16LE(method, 8);
      local.writeUInt16LE(0x0021, 12); // Fixed 1980-01-01, independent of source mtimes.
      local.writeUInt32LE(crc, 14);
      local.writeUInt32LE(compressed.length, 18);
      local.writeUInt32LE(file.size, 22);
      local.writeUInt16LE(name.length, 26);
      const entry = Buffer.alloc(46);
      entry.writeUInt32LE(0x02014b50, 0);
      entry.writeUInt16LE(0x0314, 4); // Unix creator preserves the portable executable modes.
      local.copy(entry, 6, 4, 28);
      const unixMode = (file.directory ? 0o040000 : 0o100000) | file.mode;
      entry.writeUInt32LE(((unixMode << 16) | (file.directory ? 0x10 : 0)) >>> 0, 38);
      entry.writeUInt32LE(offset, 42);
      central.push(entry, name);
      append(local); append(name); append(compressed);
    }
    const centralOffset = offset;
    for (const bytes of central) append(bytes);
    const end = Buffer.alloc(22);
    end.writeUInt32LE(0x06054b50, 0);
    end.writeUInt16LE(entries.length, 8);
    end.writeUInt16LE(entries.length, 10);
    end.writeUInt32LE(offset - centralOffset, 12);
    end.writeUInt32LE(centralOffset, 16);
    append(end);
    fchmodSync(descriptor, 0o644);
    closeSync(descriptor); descriptor = undefined;
    assertUnlinkedPath(zipPath, "Upload ZIP");
    // Publish only a complete archive, without replacing an existing destination.
    linkSync(stagedZip, zipPath);
    return { zipPath, zipBytes: offset, zipSha256: hash.digest("hex") };
  } finally {
    if (descriptor !== undefined) closeSync(descriptor);
    rmSync(stagingRoot, { recursive: true, force: true });
  }
}

function verifyExisting(plan) {
  const targetRoot = path.dirname(plan.payloadPath);
  const containerFiles = [".gitignore", ".npmignore", PLUGIN_NAME, ...(plan.portable ? [DISTRIBUTION_MANIFEST] : [])];
  if (!statSync(targetRoot).isDirectory() || readdirSync(targetRoot).sort().join("\n") !== containerFiles.sort().join("\n")) {
    throw new Error(`Refusing occupied immutable payload directory: ${targetRoot}`);
  }
  for (const marker of [".gitignore", ".npmignore"]) {
    if (regularFileBytes(targetRoot, marker).contents.toString("utf8") !== "*\n") throw new Error(`Immutable payload container marker changed: ${marker}`);
  }
  if (plan.portable) {
    const manifest = regularFileBytes(targetRoot, DISTRIBUTION_MANIFEST);
    if (manifest.mode !== 0o644 || !manifest.contents.equals(plan.receipt)) throw new Error("Portable distribution manifest changed.");
  }
  const expected = new Map(plan.files.map((file) => [file.path, { sha256: file.sha256, mode: file.mode }]));
  if (!plan.portable) expected.set(RECEIPT_PATH, { sha256: digest(plan.receipt), mode: 0o644 });
  const found = new Set();
  function visit(directory, prefix = "") {
    assertUnlinkedPath(directory, "Existing payload");
    const info = lstatSync(directory);
    if ((typeof process.getuid === "function" && info.uid !== process.getuid()) ||
        (process.platform !== "win32" && (info.mode & 0o022))) {
      throw new Error(`Unsafe existing payload directory ownership or permissions: ${directory}`);
    }
    for (const entry of readdirSync(directory, { withFileTypes: true })) {
      const relative = prefix ? `${prefix}/${entry.name}` : entry.name;
      const selected = path.join(directory, entry.name);
      if (entry.isSymbolicLink()) throw new Error(`Existing immutable payload contains a link: ${relative}`);
      if (entry.isDirectory()) {
        if (isForbiddenPayloadPath(`${relative}/placeholder`)) throw new Error(`Existing immutable payload contains a forbidden directory: ${relative}`);
        visit(selected, relative);
      } else {
        if (!expected.has(relative) || found.has(relative)) throw new Error(`Existing immutable payload contains an unexpected file: ${relative}`);
        const actual = regularFileBytes(plan.payloadPath, relative);
        const pinned = expected.get(relative);
        if (digest(actual.contents) !== pinned.sha256 || actual.mode !== pinned.mode) {
          throw new Error(`Existing immutable payload changed: ${relative}`);
        }
        found.add(relative);
      }
    }
  }
  visit(plan.payloadPath);
  if (found.size !== expected.size) throw new Error("Existing immutable payload is incomplete; choose another output root or restore it explicitly.");
}

/** Stage a local update payload or an explicitly selected portable distribution. */
export function preparePluginPayload(options = {}) {
  const plan = buildPlan(options);
  const targetRoot = path.dirname(plan.payloadPath);
  let zipPath;
  if (options.zip) {
    const release = plan.sourceVersion.split("+")[0];
    if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(release)) throw new Error("Upload ZIP requires a semantic release version.");
    zipPath = path.join(plan.outputRoot, `modretro-chromatic-plugin-for-codex-${release}-${plan.target ?? "universal"}-upload.zip`);
    assertUnlinkedPath(zipPath, "Upload ZIP");
    if (inspect(zipPath)) throw new Error(`Upload ZIP already exists; use a new output root: ${zipPath}`);
  }
  let reused = false;
  if (inspect(targetRoot)) {
    verifyExisting(plan);
    reused = true;
  } else if (!options.dryRun) {
    mkdirSync(plan.outputRoot, { recursive: true });
    assertUnlinkedPath(plan.outputRoot, "Payload output root");
    const stagingRoot = mkdtempSync(path.join(plan.outputRoot, ".staging-"));
    const stagingPayload = path.join(stagingRoot, PLUGIN_NAME);
    try {
      for (const marker of [".gitignore", ".npmignore"]) writePayloadFile(path.join(stagingRoot, marker), "*\n", 0o644);
      if (plan.portable) writePayloadFile(path.join(stagingRoot, DISTRIBUTION_MANIFEST), plan.receipt, 0o644);
      createPayloadDirectory(stagingPayload);
      const stagedFiles = [...plan.files, ...(plan.portable ? [] : [{ path: RECEIPT_PATH, contents: plan.receipt, mode: 0o644 }])];
      for (const directory of payloadDirectories(stagedFiles)) createPayloadDirectory(path.join(stagingPayload, directory));
      for (const file of stagedFiles) {
        const destination = path.join(stagingPayload, ...file.path.split("/"));
        writePayloadFile(destination, file.contents, file.mode);
      }
      try { renameSync(stagingRoot, targetRoot); }
      catch (error) {
        if (!inspect(targetRoot)) throw error;
        verifyExisting(plan);
        reused = true;
      }
    } finally {
      if (inspect(stagingRoot)) rmSync(stagingRoot, { recursive: true, force: true });
    }
    verifyExisting(plan);
  }
  const archive = zipPath ? options.dryRun ? { zipPath } : writeUploadZip(plan, zipPath) : {};
  return {
    schemaVersion: 1, pluginName: PLUGIN_NAME, sourceVersion: plan.sourceVersion, version: plan.version, sourceRoot: plan.sourceRoot,
    payloadId: plan.payloadId, payloadPath: plan.payloadPath,
    ...(plan.portable ? {
      kind: "portable-distribution", target: plan.target ?? "universal",
      manifestPath: path.join(targetRoot, DISTRIBUTION_MANIFEST), manifestBytes: plan.receipt.length,
    } : { runtimeRoot: plan.runtimeRoot, toolchainRoot: plan.toolchainRoot, nodeExecutable: plan.nodeExecutable }),
    fileCount: plan.files.length + (plan.portable ? 0 : 1), totalBytes: plan.totalBytes, reused, dryRun: Boolean(options.dryRun),
    ...archive,
  };
}

function parseArguments(args) {
  const result = {};
  const names = { "--source": "source", "--runtime-root": "runtimeRoot", "--toolchain-root": "toolchainRoot", "--output-root": "outputRoot", "--target": "target" };
  for (let index = 0; index < args.length; index++) {
    const arg = args[index];
    if (arg === "--dry-run") result.dryRun = true;
    else if (arg === "--portable") result.portable = true;
    else if (arg === "--self-contained") result.selfContained = true;
    else if (arg === "--compact") result.compact = true;
    else if (arg === "--zip") result.zip = true;
    else if (arg === "--json") result.json = true;
    else if (names[arg] && args[index + 1] && !args[index + 1].startsWith("--")) result[names[arg]] = args[++index];
    else throw new Error(`Unknown or incomplete argument: ${arg}`);
  }
  return result;
}

if (process.argv[1] && realpathSync(path.resolve(process.argv[1])) === realpathSync(fileURLToPath(import.meta.url))) {
  try {
    const options = parseArguments(process.argv.slice(2));
    const result = preparePluginPayload(options);
    if (options.json) process.stdout.write(`${JSON.stringify(result)}\n`);
    else {
      process.stdout.write(`${result.dryRun ? "Would prepare" : result.reused ? "Verified existing" : "Prepared"} ${options.portable ? "portable distribution" : "local plugin payload"}: ${result.payloadPath}\n${result.fileCount} files, ${result.totalBytes} bytes; ${options.portable ? `target: ${result.target}` : `runtime: ${result.runtimeRoot}`}\n`);
      if (result.zipPath) process.stdout.write(`${result.dryRun ? "Would write" : "Upload ZIP"}: ${result.zipPath}\n${result.zipSha256 ? `SHA-256: ${result.zipSha256}\n` : ""}Distribution receipt (not uploaded): ${result.manifestPath}\n`);
    }
  } catch (error) {
    process.stderr.write(`Plugin payload preparation failed: ${error.message}\n`);
    process.exitCode = 1;
  }
}

SHA-256: c06a16b5d0b24d5826ca40a86308cef91e27572b2bb98d352910f69a55d1111d