"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_WINDOWS_BIN_DIRECTORY = exports.DEFAULT_WINDOWS_INSTALL_DIRECTORY = exports.DEFAULT_POSIX_BIN_DIRECTORY = exports.DEFAULT_POSIX_INSTALL_DIRECTORY = exports.RELEASE_DOWNLOAD_BUNDLE_DIRECTORY = exports.RELEASE_INSTALLATION_README_FILENAME = exports.DEFAULT_RELEASE_DOWNLOAD_ORIGIN = exports.CURRENT_RELEASE_COMPATIBILITY = exports.RELEASE_SUCCESS_MARKER_FILE = void 0;
exports.resolveReleaseOutputState = resolveReleaseOutputState;
exports.prepareFreshReleaseBuildDirectory = prepareFreshReleaseBuildDirectory;
exports.writeReleaseSuccessMarker = writeReleaseSuccessMarker;
exports.readReleaseSuccessMarker = readReleaseSuccessMarker;
exports.createDryRunOperation = createDryRunOperation;
exports.createSkippedStagingPayload = createSkippedStagingPayload;
exports.createNotApplicableStagingPayload = createNotApplicableStagingPayload;
exports.createSkippedArchivePayloads = createSkippedArchivePayloads;
exports.createNotApplicableArchivePayloads = createNotApplicableArchivePayloads;
exports.createSkippedArtifactChecksums = createSkippedArtifactChecksums;
exports.createSkippedInstallerAssets = createSkippedInstallerAssets;
exports.createNotApplicableInstallerAssets = createNotApplicableInstallerAssets;
exports.createNotApplicableArtifactChecksums = createNotApplicableArtifactChecksums;
exports.createSkippedReleaseManifest = createSkippedReleaseManifest;
exports.createSkippedInstallationReadme = createSkippedInstallationReadme;
exports.createSkippedDownloadBundle = createSkippedDownloadBundle;
exports.createNotApplicableInstallationReadme = createNotApplicableInstallationReadme;
exports.createNotApplicableDownloadBundle = createNotApplicableDownloadBundle;
exports.createNotApplicableReleaseManifest = createNotApplicableReleaseManifest;
exports.populateReleaseStagingPayload = populateReleaseStagingPayload;
exports.populateReleaseArchivePayloads = populateReleaseArchivePayloads;
exports.populateReleaseInstallerAssets = populateReleaseInstallerAssets;
exports.populateReleaseArtifactChecksums = populateReleaseArtifactChecksums;
exports.validateReleaseArtifactChecksums = validateReleaseArtifactChecksums;
exports.populateReleaseManifest = populateReleaseManifest;
exports.populateReleaseInstallationReadme = populateReleaseInstallationReadme;
exports.populateReleaseInstallationReadmeFromBuildMetadata = populateReleaseInstallationReadmeFromBuildMetadata;
exports.populateReleaseDownloadBundle = populateReleaseDownloadBundle;
exports.validateReleaseInstallationReadme = validateReleaseInstallationReadme;
exports.validateReleaseManifest = validateReleaseManifest;
exports.validateReleaseInstallerSafety = validateReleaseInstallerSafety;
exports.parseRetentionKeep = parseRetentionKeep;
exports.applyReleaseRetention = applyReleaseRetention;
exports.applyReleaseCleanup = applyReleaseCleanup;
exports.normalizeReleaseManifestCompatibility = normalizeReleaseManifestCompatibility;
const node_crypto_1 = __importDefault(require("node:crypto"));
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const node_zlib_1 = __importDefault(require("node:zlib"));
const errors_1 = require("../../../lib/errors");
const license_guidance_1 = require("../../../lib/license/license-guidance");
const default_assets_1 = require("../../../lib/workspace-init/default-assets");
const workspace_compatibility_1 = require("../../../lib/workspace-compatibility");
const version_1 = require("./version");
exports.RELEASE_SUCCESS_MARKER_FILE = ".day-shift-release-success.json";
exports.CURRENT_RELEASE_COMPATIBILITY = {
    workspaceMetadata: {
        minimum: 1,
        maximum: 1
    },
    artifactSchemas: {
        specification: {
            minimum: 1,
            maximum: 1
        },
        planning: {
            minimum: 1,
            maximum: 1
        },
        prompt: {
            minimum: 1,
            maximum: 1
        },
        template: {
            minimum: 1,
            maximum: 1
        },
        promptManifest: {
            minimum: 1,
            maximum: 1
        },
        templateManifest: {
            minimum: 1,
            maximum: 1
        },
        releaseManifest: {
            minimum: 1,
            maximum: 1
        },
        releaseRuntimeMetadata: {
            minimum: 1,
            maximum: 1
        }
    }
};
function resolveReleaseOutputState(options) {
    const repositoryRoot = node_path_1.default.resolve(options.repositoryRoot);
    const releaseRoot = resolvePathWithinRepository({
        repositoryRoot,
        value: options.outputDir,
        fallback: "release",
        label: "release output root"
    });
    const versionDirectory = options.version === null
        ? null
        : node_path_1.default.join(releaseRoot, options.version);
    const stagingDirectory = versionDirectory === null
        ? null
        : node_path_1.default.join(versionDirectory, "staging");
    return {
        releaseRoot,
        releaseRootRelative: toRepositoryRelativePath(repositoryRoot, releaseRoot),
        versionDirectory,
        versionDirectoryRelative: versionDirectory === null
            ? null
            : toRepositoryRelativePath(repositoryRoot, versionDirectory),
        stagingDirectory,
        stagingDirectoryRelative: stagingDirectory === null
            ? null
            : toRepositoryRelativePath(repositoryRoot, stagingDirectory),
        successMarkerPath: versionDirectory === null
            ? null
            : node_path_1.default.join(versionDirectory, exports.RELEASE_SUCCESS_MARKER_FILE),
        successMarkerPathRelative: versionDirectory === null
            ? null
            : toRepositoryRelativePath(repositoryRoot, node_path_1.default.join(versionDirectory, exports.RELEASE_SUCCESS_MARKER_FILE))
    };
}
function prepareFreshReleaseBuildDirectory(state) {
    if (state.versionDirectory === null ||
        state.versionDirectoryRelative === null ||
        state.stagingDirectory === null ||
        state.stagingDirectoryRelative === null) {
        throw (0, errors_1.createUsageError)("Release build requires a resolved version directory.", [
            "Exit behavior: exits before generated release output directories or artifacts are created."
        ]);
    }
    if (node_fs_1.default.existsSync(state.versionDirectory)) {
        throw (0, errors_1.createUsageError)(`Release output directory already exists: ${state.versionDirectoryRelative}`, [
            "Reason: stale-or-partial-version-directory",
            "Recovery: choose a new release version or run release cleanup for generated local output.",
            "Exit behavior: exits before generated release output directories or artifacts are created."
        ]);
    }
    node_fs_1.default.mkdirSync(state.stagingDirectory, { recursive: true });
    return {
        kind: "fresh-staging-directory",
        dryRun: false,
        changed: true,
        removedPaths: [],
        retainedPaths: [state.versionDirectoryRelative, state.stagingDirectoryRelative],
        message: `Created fresh local release staging directory ${state.stagingDirectoryRelative}.`
    };
}
function writeReleaseSuccessMarker(options) {
    if (options.state.versionDirectory === null ||
        options.state.successMarkerPath === null ||
        options.state.successMarkerPathRelative === null) {
        throw (0, errors_1.createUsageError)("Release success marker requires a resolved version directory.", [
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    const marker = {
        marker: "day-shift-release-success",
        version: options.version,
        command: options.command,
        outputRoot: options.state.releaseRootRelative,
        completedAt: new Date().toISOString()
    };
    node_fs_1.default.writeFileSync(options.state.successMarkerPath, `${JSON.stringify(marker, null, 2)}\n`);
    return {
        present: true,
        valid: true,
        path: options.state.successMarkerPathRelative,
        version: marker.version,
        completedAt: marker.completedAt
    };
}
function readReleaseSuccessMarker(state) {
    if (state.successMarkerPath === null || state.successMarkerPathRelative === null) {
        return {
            present: false,
            valid: false,
            path: null,
            version: null,
            completedAt: null
        };
    }
    if (!node_fs_1.default.existsSync(state.successMarkerPath)) {
        return {
            present: false,
            valid: false,
            path: state.successMarkerPathRelative,
            version: null,
            completedAt: null
        };
    }
    try {
        const marker = JSON.parse(node_fs_1.default.readFileSync(state.successMarkerPath, "utf8"));
        const valid = marker.marker === "day-shift-release-success" &&
            typeof marker.version === "string" &&
            marker.version.length > 0 &&
            typeof marker.completedAt === "string" &&
            marker.completedAt.length > 0;
        return {
            present: true,
            valid,
            path: state.successMarkerPathRelative,
            version: typeof marker.version === "string" ? marker.version : null,
            completedAt: typeof marker.completedAt === "string" ? marker.completedAt : null
        };
    }
    catch {
        return {
            present: true,
            valid: false,
            path: state.successMarkerPathRelative,
            version: null,
            completedAt: null
        };
    }
}
function createDryRunOperation(kind, message) {
    return {
        kind,
        dryRun: true,
        changed: false,
        removedPaths: [],
        retainedPaths: [],
        message
    };
}
function createSkippedStagingPayload(state) {
    return {
        status: "skipped-dry-run",
        stagingDirectory: state.stagingDirectoryRelative,
        files: [],
        launchers: [],
        productionDependencies: [],
        noticeFiles: [],
        validation: null
    };
}
function createNotApplicableStagingPayload() {
    return {
        status: "not-applicable",
        stagingDirectory: null,
        files: [],
        launchers: [],
        productionDependencies: [],
        noticeFiles: [],
        validation: null
    };
}
function createSkippedArchivePayloads(state) {
    return {
        status: "skipped-dry-run",
        rootDirectory: state.versionDirectoryRelative === null
            ? null
            : `${state.versionDirectoryRelative}/archive-payloads`,
        targets: []
    };
}
function createNotApplicableArchivePayloads() {
    return {
        status: "not-applicable",
        rootDirectory: null,
        targets: []
    };
}
function createSkippedArtifactChecksums(state) {
    const bundleDirectory = resolveReleaseDownloadBundleRelativeDirectory(state);
    return {
        status: "skipped-dry-run",
        checksumJsonPath: bundleDirectory === null
            ? null
            : `${bundleDirectory}/artifact-checksums.json`,
        checksumTextPath: bundleDirectory === null
            ? null
            : `${bundleDirectory}/checksums.sha256`,
        artifacts: []
    };
}
function createSkippedInstallerAssets(state) {
    const bundleDirectory = resolveReleaseDownloadBundleRelativeDirectory(state);
    return {
        status: "skipped-dry-run",
        origin: exports.DEFAULT_RELEASE_DOWNLOAD_ORIGIN,
        assets: bundleDirectory === null
            ? []
            : [
                {
                    target: "posix-installer",
                    filename: "install.sh",
                    path: `${bundleDirectory}/install.sh`,
                    lineEndings: "lf"
                },
                {
                    target: "windows-installer",
                    filename: "install.ps1",
                    path: `${bundleDirectory}/install.ps1`,
                    lineEndings: "crlf"
                },
                {
                    target: "posix-installer",
                    filename: "uninstall.sh",
                    path: `${bundleDirectory}/uninstall.sh`,
                    lineEndings: "lf"
                },
                {
                    target: "windows-installer",
                    filename: "uninstall.ps1",
                    path: `${bundleDirectory}/uninstall.ps1`,
                    lineEndings: "crlf"
                }
            ]
    };
}
function createNotApplicableInstallerAssets() {
    return {
        status: "not-applicable",
        origin: null,
        assets: []
    };
}
function createNotApplicableArtifactChecksums() {
    return {
        status: "not-applicable",
        checksumJsonPath: null,
        checksumTextPath: null,
        artifacts: []
    };
}
exports.DEFAULT_RELEASE_DOWNLOAD_ORIGIN = "https://tnsds.tech/downloads/";
exports.RELEASE_INSTALLATION_README_FILENAME = "INSTALL.md";
exports.RELEASE_DOWNLOAD_BUNDLE_DIRECTORY = "download-bundle";
exports.DEFAULT_POSIX_INSTALL_DIRECTORY = "$HOME/.local/day-shift";
exports.DEFAULT_POSIX_BIN_DIRECTORY = "$HOME/.local/bin";
exports.DEFAULT_WINDOWS_INSTALL_DIRECTORY = "$HOME\\AppData\\Local\\DayShift";
exports.DEFAULT_WINDOWS_BIN_DIRECTORY = "$HOME\\AppData\\Local\\Microsoft\\WindowsApps";
const PUBLIC_CLI_EXCLUDED_PRIVATE_RUNTIME_FILES = [
    "dist/lib/license/license-issuer.js",
    "dist/lib/license/license-issuer.js.map",
    "dist/lib/license/license-issuer.d.ts"
];
function createSkippedReleaseManifest(state) {
    const bundleDirectory = resolveReleaseDownloadBundleRelativeDirectory(state);
    return {
        status: "skipped-dry-run",
        manifestPath: bundleDirectory === null
            ? null
            : `${bundleDirectory}/release-manifest.json`,
        data: null
    };
}
function createSkippedInstallationReadme(state) {
    const bundleDirectory = resolveReleaseDownloadBundleRelativeDirectory(state);
    return {
        status: "skipped-dry-run",
        path: bundleDirectory === null
            ? null
            : `${bundleDirectory}/${exports.RELEASE_INSTALLATION_README_FILENAME}`
    };
}
function createSkippedDownloadBundle(state) {
    return {
        status: "skipped-dry-run",
        directory: resolveReleaseDownloadBundleRelativeDirectory(state),
        files: []
    };
}
function createNotApplicableInstallationReadme() {
    return {
        status: "not-applicable",
        path: null
    };
}
function createNotApplicableDownloadBundle() {
    return {
        status: "not-applicable",
        directory: null,
        files: []
    };
}
function createNotApplicableReleaseManifest() {
    return {
        status: "not-applicable",
        manifestPath: null,
        data: null
    };
}
function populateReleaseStagingPayload(options) {
    if (options.state.stagingDirectory === null || options.state.stagingDirectoryRelative === null) {
        throw (0, errors_1.createUsageError)("Release staging payload requires a resolved staging directory.", [
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    const packageJsonPath = node_path_1.default.join(options.repositoryRoot, "package.json");
    const distPath = node_path_1.default.join(options.repositoryRoot, "dist");
    const distEntrypointPath = node_path_1.default.join(options.repositoryRoot, "dist", "cli", "index.js");
    if (!node_fs_1.default.existsSync(packageJsonPath)) {
        throw (0, errors_1.createUsageError)("Release staging payload requires package.json.", [
            "Reason: missing-package-metadata",
            "Recovery: restore package.json before running release build.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (!node_fs_1.default.existsSync(distPath) || !node_fs_1.default.statSync(distPath).isDirectory() || !node_fs_1.default.existsSync(distEntrypointPath)) {
        throw (0, errors_1.createUsageError)("Release staging payload requires compiled dist output.", [
            "Reason: missing-compiled-dist",
            "Required entrypoint: dist/cli/index.js",
            "Recovery: fix the production build so it emits compiled files under dist/.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    const packageJson = JSON.parse(node_fs_1.default.readFileSync(packageJsonPath, "utf8"));
    const dependencies = normalizeStringRecord(packageJson.dependencies);
    const productionDependencyPlan = resolveProductionDependencyClosure({
        repositoryRoot: options.repositoryRoot,
        dependencyNames: Object.keys(dependencies)
    });
    const productionDependencyNames = [...productionDependencyPlan.keys()].sort();
    const missingDependencies = productionDependencyNames.filter((dependencyName) => productionDependencyPlan.get(dependencyName) === null);
    if (missingDependencies.length > 0) {
        throw (0, errors_1.createUsageError)("Release staging payload requires installed production dependency contents.", [
            "Reason: missing-production-dependencies",
            `Missing dependencies: ${missingDependencies.join(", ")}`,
            "Recovery: install locked dependencies before running release build.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    node_fs_1.default.cpSync(distPath, node_path_1.default.join(options.state.stagingDirectory, "dist"), { recursive: true });
    removePublicCliExcludedPrivateRuntimeFiles(options.state.stagingDirectory);
    node_fs_1.default.writeFileSync(node_path_1.default.join(options.state.stagingDirectory, "package.json"), `${JSON.stringify(createRuntimePackageJson({
        packageJson,
        version: options.version,
        installedCommandName: options.installedCommandName
    }), null, 2)}\n`);
    node_fs_1.default.writeFileSync(node_path_1.default.join(options.state.stagingDirectory, ".day-shift-release.json"), `${JSON.stringify({
        name: readOptionalString(packageJson.name),
        schemaVersion: 1,
        version: options.version,
        installedCommandName: options.installedCommandName,
        runtimeModel: "system-node",
        requiredNodeVersion: typeof packageJson.engines === "object" && packageJson.engines !== null
            ? readOptionalString(packageJson.engines.node)
            : null,
        compatibility: exports.CURRENT_RELEASE_COMPATIBILITY,
        releaseNotesSourceSummary: options.releaseNotesSourceSummary ?? null
    }, null, 2)}\n`);
    const launchers = writeReleaseLaunchers({
        stagingDirectory: options.state.stagingDirectory,
        stagingDirectoryRelative: options.state.stagingDirectoryRelative,
        installedCommandName: options.installedCommandName
    });
    for (const dependencyName of productionDependencyNames) {
        const sourceDirectory = productionDependencyPlan.get(dependencyName);
        if (sourceDirectory === null || sourceDirectory === undefined) {
            continue;
        }
        copyDirectoryFiltered({
            sourceDirectory,
            destinationDirectory: node_path_1.default.join(options.state.stagingDirectory, "node_modules", ...dependencyName.split("/")),
            payloadRoot: options.state.stagingDirectory
        });
    }
    copyRepositorySharedAuthoringDirectories({
        repositoryRoot: options.repositoryRoot,
        stagingDirectory: options.state.stagingDirectory
    });
    const noticeFiles = copyRepositoryNoticeFiles({
        repositoryRoot: options.repositoryRoot,
        stagingDirectory: options.state.stagingDirectory
    });
    const files = collectFiles(options.state.stagingDirectory)
        .map((filePath) => toRepositoryRelativePath(options.state.stagingDirectory, filePath))
        .sort();
    const validation = validateReleasePayloadContent({
        payload: "staging",
        files,
        requiredFiles: [
            "dist/cli/index.js",
            "package.json",
            ".day-shift-release.json",
            `bin/${options.installedCommandName}`,
            `bin/${options.installedCommandName}.cmd`,
            `bin/${options.installedCommandName}.ps1`
        ]
    });
    return {
        status: "created",
        stagingDirectory: options.state.stagingDirectoryRelative,
        files,
        launchers,
        productionDependencies: productionDependencyNames,
        noticeFiles,
        validation
    };
}
function resolveProductionDependencyClosure(options) {
    const resolved = new Map();
    const pending = options.dependencyNames
        .sort()
        .map((dependencyName) => ({ dependencyName, parentDirectory: options.repositoryRoot }));
    while (pending.length > 0) {
        const candidate = pending.shift();
        const sourceDirectory = resolveInstalledDependencyDirectory({
            repositoryRoot: options.repositoryRoot,
            parentDirectory: candidate.parentDirectory,
            dependencyName: candidate.dependencyName
        });
        const existing = resolved.get(candidate.dependencyName);
        if (sourceDirectory === null) {
            if (existing === undefined) {
                resolved.set(candidate.dependencyName, null);
            }
            continue;
        }
        if (existing !== undefined && existing !== null) {
            if (node_fs_1.default.realpathSync(existing) !== node_fs_1.default.realpathSync(sourceDirectory)) {
                throw (0, errors_1.createUsageError)("Release staging payload cannot flatten conflicting production dependency versions.", [
                    "Reason: conflicting-production-dependencies",
                    `Dependency: ${candidate.dependencyName}`,
                    "Recovery: use a production deployment layout that preserves nested dependency versions.",
                    "Exit behavior: exits before successful-release state is recorded."
                ]);
            }
            continue;
        }
        resolved.set(candidate.dependencyName, sourceDirectory);
        const dependencyPackageJson = JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(sourceDirectory, "package.json"), "utf8"));
        const childDependencies = Object.keys(normalizeStringRecord(dependencyPackageJson.dependencies)).sort();
        for (const dependencyName of childDependencies) {
            pending.push({ dependencyName, parentDirectory: sourceDirectory });
        }
    }
    return resolved;
}
function resolveInstalledDependencyDirectory(options) {
    const dependencySegments = options.dependencyName.split("/");
    const candidates = [
        node_path_1.default.join(options.parentDirectory, "node_modules", ...dependencySegments),
        node_path_1.default.join(node_path_1.default.dirname(options.parentDirectory), ...dependencySegments),
        node_path_1.default.join(options.repositoryRoot, "node_modules", ...dependencySegments)
    ];
    for (const candidate of candidates) {
        if (node_fs_1.default.existsSync(node_path_1.default.join(candidate, "package.json"))) {
            return node_fs_1.default.realpathSync(candidate);
        }
    }
    return null;
}
function populateReleaseArchivePayloads(options) {
    if (options.state.versionDirectory === null ||
        options.state.versionDirectoryRelative === null ||
        options.state.stagingDirectory === null) {
        throw (0, errors_1.createUsageError)("Release archive payloads require resolved version and staging directories.", [
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (options.stagingPayload.status !== "created") {
        return createNotApplicableArchivePayloads();
    }
    const packageSlug = normalizeArchiveNamePart(options.packageName ?? "day-shift");
    const downloadBundleDirectory = resolveReleaseDownloadBundleDirectory(options.state);
    const downloadBundleRelativeDirectory = resolveReleaseDownloadBundleRelativeDirectory(options.state);
    if (downloadBundleDirectory === null || downloadBundleRelativeDirectory === null) {
        throw (0, errors_1.createUsageError)("Release archive payloads require a resolved download bundle directory.", [
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    const archivePayloadRoot = node_path_1.default.join(options.state.versionDirectory, "archive-payloads");
    const archivePayloadRootRelative = `${options.state.versionDirectoryRelative}/archive-payloads`;
    const targets = [
        {
            target: "macos-linux",
            runtimeModel: "system-node",
            rootDirectoryName: `${packageSlug}-${options.version}-macos-linux-system-node`,
            payloadDirectory: "",
            archiveFilename: `${packageSlug}-${options.version}-macos-linux-system-node.tar.gz`,
            archivePath: "",
            launcherPaths: [`bin/${options.installedCommandName}`],
            files: [],
            validation: null
        },
        {
            target: "windows",
            runtimeModel: "system-node",
            rootDirectoryName: `${packageSlug}-${options.version}-windows-system-node`,
            payloadDirectory: "",
            archiveFilename: `${packageSlug}-${options.version}-windows-system-node.zip`,
            archivePath: "",
            launcherPaths: [`bin/${options.installedCommandName}.cmd`, `bin/${options.installedCommandName}.ps1`],
            files: [],
            validation: null
        }
    ];
    for (const target of targets) {
        const targetDirectory = node_path_1.default.join(archivePayloadRoot, target.rootDirectoryName);
        node_fs_1.default.rmSync(targetDirectory, { recursive: true, force: true });
        node_fs_1.default.mkdirSync(node_path_1.default.dirname(targetDirectory), { recursive: true });
        node_fs_1.default.cpSync(options.state.stagingDirectory, targetDirectory, { recursive: true });
        if (target.target === "macos-linux") {
            removeIfExists(node_path_1.default.join(targetDirectory, "bin", `${options.installedCommandName}.cmd`));
            removeIfExists(node_path_1.default.join(targetDirectory, "bin", `${options.installedCommandName}.ps1`));
        }
        else {
            removeIfExists(node_path_1.default.join(targetDirectory, "bin", options.installedCommandName));
        }
        copyArchiveRootDocumentation({
            versionDirectory: options.state.versionDirectory,
            targetDirectory
        });
        target.payloadDirectory = `${archivePayloadRootRelative}/${target.rootDirectoryName}`;
        target.files = collectFiles(targetDirectory)
            .map((filePath) => toRepositoryRelativePath(targetDirectory, filePath))
            .sort();
        target.validation = validateReleasePayloadContent({
            payload: "archive",
            files: target.files,
            requiredFiles: [
                "dist/cli/index.js",
                "package.json",
                ".day-shift-release.json",
                exports.RELEASE_INSTALLATION_README_FILENAME,
                "release-notes.md",
                ...target.launcherPaths
            ]
        });
        target.archivePath = `${downloadBundleRelativeDirectory}/${target.archiveFilename}`;
        node_fs_1.default.mkdirSync(downloadBundleDirectory, { recursive: true });
        const archivePath = node_path_1.default.join(downloadBundleDirectory, target.archiveFilename);
        if (target.target === "macos-linux") {
            writeDeterministicTarGzipArchive({
                sourceDirectory: targetDirectory,
                rootDirectoryName: target.rootDirectoryName,
                filePaths: target.files,
                archivePath
            });
        }
        else {
            writeDeterministicZipArchive({
                sourceDirectory: targetDirectory,
                rootDirectoryName: target.rootDirectoryName,
                filePaths: target.files,
                archivePath
            });
        }
    }
    return {
        status: "created",
        rootDirectory: archivePayloadRootRelative,
        targets
    };
}
function populateReleaseInstallerAssets(options) {
    if (options.state.versionDirectory === null ||
        options.state.versionDirectoryRelative === null) {
        throw (0, errors_1.createUsageError)("Release installer assets require a resolved version directory.", [
            "Exit behavior: exits before generated installer assets are created."
        ]);
    }
    const origin = normalizeDownloadOrigin(options.officialOrigin ?? exports.DEFAULT_RELEASE_DOWNLOAD_ORIGIN);
    const bundleDirectory = resolveReleaseDownloadBundleDirectory(options.state);
    const bundleDirectoryRelative = resolveReleaseDownloadBundleRelativeDirectory(options.state);
    if (bundleDirectory === null || bundleDirectoryRelative === null) {
        throw (0, errors_1.createUsageError)("Release installer assets require a resolved download bundle directory.", [
            "Exit behavior: exits before generated installer assets are created."
        ]);
    }
    node_fs_1.default.mkdirSync(bundleDirectory, { recursive: true });
    const posixInstallerPath = node_path_1.default.join(bundleDirectory, "install.sh");
    node_fs_1.default.writeFileSync(posixInstallerPath, renderPosixInstallerAsset({
        defaultVersion: options.version,
        installedCommandName: options.installedCommandName,
        officialOrigin: origin
    }));
    const windowsInstallerPath = node_path_1.default.join(bundleDirectory, "install.ps1");
    node_fs_1.default.writeFileSync(windowsInstallerPath, renderWindowsInstallerAsset({
        defaultVersion: options.version,
        installedCommandName: options.installedCommandName,
        officialOrigin: origin
    }));
    const posixUninstallerPath = node_path_1.default.join(bundleDirectory, "uninstall.sh");
    node_fs_1.default.writeFileSync(posixUninstallerPath, renderPosixUninstallerAsset({
        installedCommandName: options.installedCommandName
    }));
    const windowsUninstallerPath = node_path_1.default.join(bundleDirectory, "uninstall.ps1");
    node_fs_1.default.writeFileSync(windowsUninstallerPath, renderWindowsUninstallerAsset({
        installedCommandName: options.installedCommandName
    }));
    return {
        status: "created",
        origin,
        assets: [
            {
                target: "posix-installer",
                filename: "install.sh",
                path: `${bundleDirectoryRelative}/install.sh`,
                lineEndings: "lf"
            },
            {
                target: "windows-installer",
                filename: "install.ps1",
                path: `${bundleDirectoryRelative}/install.ps1`,
                lineEndings: "crlf"
            },
            {
                target: "posix-installer",
                filename: "uninstall.sh",
                path: `${bundleDirectoryRelative}/uninstall.sh`,
                lineEndings: "lf"
            },
            {
                target: "windows-installer",
                filename: "uninstall.ps1",
                path: `${bundleDirectoryRelative}/uninstall.ps1`,
                lineEndings: "crlf"
            }
        ]
    };
}
function populateReleaseArtifactChecksums(options) {
    if (options.state.versionDirectory === null ||
        options.state.versionDirectoryRelative === null) {
        throw (0, errors_1.createUsageError)("Release artifact checksums require a resolved version directory.", [
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (options.archivePayloads.status !== "created") {
        return createNotApplicableArtifactChecksums();
    }
    const archiveRecords = options.archivePayloads.targets.map((target) => createChecksumRecord({
        versionDirectory: options.state.versionDirectory,
        versionDirectoryRelative: options.state.versionDirectoryRelative,
        kind: "archive",
        target: target.target,
        artifactPath: target.archivePath
    }));
    const installerRecords = collectInstallerAssetPaths(options.state.versionDirectory)
        .map((artifactPath) => createChecksumRecord({
        versionDirectory: options.state.versionDirectory,
        versionDirectoryRelative: options.state.versionDirectoryRelative,
        kind: "installer",
        target: classifyInstallerAssetTarget(node_path_1.default.basename(artifactPath)),
        artifactPath
    }));
    const artifacts = [...archiveRecords, ...installerRecords]
        .sort((left, right) => left.path.localeCompare(right.path));
    const bundleDirectory = resolveReleaseDownloadBundleDirectory(options.state);
    const bundleDirectoryRelative = resolveReleaseDownloadBundleRelativeDirectory(options.state);
    if (bundleDirectory === null || bundleDirectoryRelative === null) {
        throw (0, errors_1.createUsageError)("Release artifact checksums require a resolved download bundle directory.", [
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    node_fs_1.default.mkdirSync(bundleDirectory, { recursive: true });
    const checksumJsonPath = node_path_1.default.join(bundleDirectory, "artifact-checksums.json");
    const checksumTextPath = node_path_1.default.join(bundleDirectory, "checksums.sha256");
    const checksumJsonPathRelative = `${bundleDirectoryRelative}/artifact-checksums.json`;
    const checksumTextPathRelative = `${bundleDirectoryRelative}/checksums.sha256`;
    node_fs_1.default.writeFileSync(checksumJsonPath, `${JSON.stringify({
        generatedBy: "day-shift-release",
        schemaVersion: 1,
        artifacts
    }, null, 2)}\n`);
    node_fs_1.default.writeFileSync(checksumTextPath, artifacts.map((artifact) => `${artifact.sha256}  ${artifact.path}`).join("\n") + "\n");
    validateReleaseArtifactChecksums({
        versionDirectory: options.state.versionDirectory,
        versionDirectoryRelative: options.state.versionDirectoryRelative,
        checksumJsonPath,
        expectedArtifactPaths: artifacts.map((artifact) => artifact.path)
    });
    return {
        status: "created",
        checksumJsonPath: checksumJsonPathRelative,
        checksumTextPath: checksumTextPathRelative,
        artifacts
    };
}
function validateReleaseArtifactChecksums(options) {
    const checksumPath = resolveArtifactPathInsideVersionDirectory({
        versionDirectory: options.versionDirectory,
        versionDirectoryRelative: options.versionDirectoryRelative,
        artifactPath: options.checksumJsonPath,
        label: "checksum record"
    });
    if (!node_fs_1.default.existsSync(checksumPath) || !node_fs_1.default.statSync(checksumPath).isFile()) {
        throw (0, errors_1.createUsageError)("Release artifact checksum record is missing.", [
            "Reason: missing-checksum-record",
            `Checksum record: ${options.checksumJsonPath}`,
            "Recovery: regenerate checksum evidence after archive and installer assets are finalized.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    const parsed = JSON.parse(node_fs_1.default.readFileSync(checksumPath, "utf8"));
    if (!Array.isArray(parsed.artifacts)) {
        throw (0, errors_1.createUsageError)("Release artifact checksum record is malformed.", [
            "Reason: malformed-checksum-record",
            `Checksum record: ${options.checksumJsonPath}`,
            "Recovery: regenerate checksum evidence with the release checksum generator.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    const records = parsed.artifacts.map((entry) => normalizeChecksumRecord(entry));
    const recordsByPath = new Map(records.map((record) => [record.path, record]));
    const missingRecords = options.expectedArtifactPaths.filter((artifactPath) => !recordsByPath.has(artifactPath));
    if (missingRecords.length > 0) {
        throw (0, errors_1.createUsageError)("Release artifact checksum record is missing expected artifacts.", [
            "Reason: absent-checksum-record",
            `Missing artifacts: ${missingRecords.join(", ")}`,
            "Recovery: regenerate checksum evidence after all expected archive and installer assets are present.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    for (const record of records) {
        const artifactPath = resolveArtifactPathInsideVersionDirectory({
            versionDirectory: options.versionDirectory,
            versionDirectoryRelative: options.versionDirectoryRelative,
            artifactPath: record.path,
            label: "artifact"
        });
        if (!node_fs_1.default.existsSync(artifactPath) || !node_fs_1.default.statSync(artifactPath).isFile()) {
            throw (0, errors_1.createUsageError)("Release artifact referenced by checksum record is missing.", [
                "Reason: missing-artifact",
                `Artifact: ${record.path}`,
                "Recovery: regenerate release archives or installer assets before checksum validation.",
                "Exit behavior: exits before successful-release state is recorded."
            ]);
        }
        const artifactBytes = node_fs_1.default.readFileSync(artifactPath);
        const actualSha256 = node_crypto_1.default.createHash("sha256").update(artifactBytes).digest("hex");
        if (record.byteSize !== artifactBytes.length) {
            throw (0, errors_1.createUsageError)("Release artifact byte size does not match checksum record.", [
                "Reason: artifact-byte-size-mismatch",
                `Artifact: ${record.path}`,
                `Expected byte size: ${record.byteSize}`,
                `Actual byte size: ${artifactBytes.length}`,
                "Recovery: regenerate checksum evidence after final artifact writes complete.",
                "Exit behavior: exits before successful-release state is recorded."
            ]);
        }
        if (record.sha256 !== actualSha256) {
            throw (0, errors_1.createUsageError)("Release artifact SHA-256 does not match checksum record.", [
                "Reason: artifact-checksum-mismatch",
                `Artifact: ${record.path}`,
                "Recovery: regenerate checksum evidence after final artifact writes complete.",
                "Exit behavior: exits before successful-release state is recorded."
            ]);
        }
    }
    return records;
}
function populateReleaseManifest(options) {
    if (options.state.versionDirectory === null ||
        options.state.versionDirectoryRelative === null) {
        throw (0, errors_1.createUsageError)("Release manifest generation requires a resolved version directory.", [
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (options.artifactChecksums.status !== "created") {
        return createNotApplicableReleaseManifest();
    }
    const bundleDirectory = resolveReleaseDownloadBundleDirectory(options.state);
    const bundleDirectoryRelative = resolveReleaseDownloadBundleRelativeDirectory(options.state);
    if (bundleDirectory === null || bundleDirectoryRelative === null) {
        throw (0, errors_1.createUsageError)("Release manifest generation requires a resolved download bundle directory.", [
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    node_fs_1.default.mkdirSync(bundleDirectory, { recursive: true });
    const manifestPath = node_path_1.default.join(bundleDirectory, "release-manifest.json");
    const manifestPathRelative = `${bundleDirectoryRelative}/release-manifest.json`;
    const releaseNotesPath = `${bundleDirectoryRelative}/release-notes.md`;
    const releaseNotesFilePath = node_path_1.default.join(bundleDirectory, "release-notes.md");
    const manifest = {
        schemaVersion: 1,
        generatedBy: "day-shift-release",
        cliVersion: requireManifestString(options.cliVersion, "CLI version", "missing-cli-version"),
        version: options.version,
        releaseDate: new Date().toISOString().slice(0, 10),
        status: "active",
        command: {
            installedName: options.installedCommandName,
            stagedCommandPath: normalizePayloadPath(options.stagedCommandPath),
            invocations: {
                development: "node dist/cli/index.js",
                stagedRelease: normalizePayloadPath(options.stagedCommandPath),
                installedUser: options.installedCommandName
            }
        },
        runtime: {
            model: "system-node",
            requiredNodeVersion: requireManifestString(options.requiredNodeVersion, "Node.js requirement", "missing-node-version-requirement"),
            productionDependenciesIncluded: true
        },
        supportedPlatforms: createReleaseManifestSupportedPlatforms(),
        installation: {
            readmePath: `${bundleDirectoryRelative}/${exports.RELEASE_INSTALLATION_README_FILENAME}`,
            defaultInstallDirectory: {
                posix: exports.DEFAULT_POSIX_INSTALL_DIRECTORY,
                windows: exports.DEFAULT_WINDOWS_INSTALL_DIRECTORY
            },
            defaultBinDirectory: {
                posix: exports.DEFAULT_POSIX_BIN_DIRECTORY,
                windows: exports.DEFAULT_WINDOWS_BIN_DIRECTORY
            }
        },
        compatibility: exports.CURRENT_RELEASE_COMPATIBILITY,
        artifacts: options.artifactChecksums.artifacts.map((artifact) => ({
            kind: artifact.kind,
            target: artifact.target,
            filename: artifact.filename,
            path: artifact.path,
            downloadPath: `downloads/${options.version}/${artifact.filename}`,
            url: createReleaseDownloadUrl(options.version, artifact.filename),
            byteSize: artifact.byteSize,
            sha256: artifact.sha256,
            runtimeModel: "system-node",
            supportedOs: getSupportedOsForManifestTarget(artifact.target),
            supportedArch: ["x64", "arm64"]
        })),
        releaseNotes: {
            path: releaseNotesPath,
            url: createReleaseDownloadUrl(options.version, "release-notes.md"),
            sourceSummary: {
                ...options.releaseNotesSourceSummary,
                status: node_fs_1.default.existsSync(releaseNotesFilePath) ? "generated" : options.releaseNotesSourceSummary.status
            }
        },
        replacement: {
            status: "active",
            replacementVersion: null,
            replacementManifestPath: null,
            reason: null
        },
        publication: {
            websiteUpload: "out-of-scope",
            publicNpmPublication: "out-of-scope"
        }
    };
    validateReleaseManifest({
        versionDirectory: options.state.versionDirectory,
        versionDirectoryRelative: options.state.versionDirectoryRelative,
        expectedVersion: options.version,
        expectedInstalledCommandName: options.installedCommandName,
        expectedStagedCommandPath: options.stagedCommandPath,
        expectedArtifactPaths: options.artifactChecksums.artifacts.map((artifact) => artifact.path),
        manifest
    });
    node_fs_1.default.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
    return {
        status: "created",
        manifestPath: manifestPathRelative,
        data: manifest
    };
}
function populateReleaseInstallationReadme(options) {
    if (options.state.versionDirectory === null ||
        options.state.versionDirectoryRelative === null) {
        throw (0, errors_1.createUsageError)("Release installation README requires a resolved version directory.", [
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    const readmePath = node_path_1.default.join(options.state.versionDirectory, exports.RELEASE_INSTALLATION_README_FILENAME);
    const readmePathRelative = `${options.state.versionDirectoryRelative}/${exports.RELEASE_INSTALLATION_README_FILENAME}`;
    const markdown = renderReleaseInstallationReadme({
        metadata: createReleaseInstallationReadmeMetadataFromManifest(options.manifest)
    });
    validateReleaseInstallationReadme({
        markdown,
        manifest: options.manifest
    });
    node_fs_1.default.writeFileSync(readmePath, markdown);
    return {
        status: "written",
        path: readmePathRelative
    };
}
function populateReleaseInstallationReadmeFromBuildMetadata(options) {
    if (options.state.versionDirectory === null ||
        options.state.versionDirectoryRelative === null) {
        throw (0, errors_1.createUsageError)("Release installation README requires a resolved version directory.", [
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    const metadata = createReleaseInstallationReadmeMetadataFromBuildMetadata({
        state: options.state,
        version: options.version,
        packageName: options.packageName,
        installedCommandName: options.installedCommandName,
        requiredNodeVersion: options.requiredNodeVersion
    });
    const bundleDirectory = resolveReleaseDownloadBundleDirectory(options.state);
    const bundleDirectoryRelative = resolveReleaseDownloadBundleRelativeDirectory(options.state);
    if (bundleDirectory === null || bundleDirectoryRelative === null) {
        throw (0, errors_1.createUsageError)("Release installation README requires a resolved download bundle directory.", [
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    node_fs_1.default.mkdirSync(bundleDirectory, { recursive: true });
    const readmePath = node_path_1.default.join(bundleDirectory, exports.RELEASE_INSTALLATION_README_FILENAME);
    const readmePathRelative = `${bundleDirectoryRelative}/${exports.RELEASE_INSTALLATION_README_FILENAME}`;
    const markdown = renderReleaseInstallationReadme({ metadata });
    validateReleaseInstallationReadme({
        markdown,
        metadata
    });
    node_fs_1.default.writeFileSync(readmePath, markdown);
    return {
        status: "written",
        path: readmePathRelative
    };
}
function populateReleaseDownloadBundle(options) {
    if (options.state.versionDirectory === null ||
        options.state.versionDirectoryRelative === null) {
        throw (0, errors_1.createUsageError)("Release download bundle requires a resolved version directory.", [
            "Exit behavior: exits before user download bundle files are created."
        ]);
    }
    if (options.archivePayloads.status !== "created" ||
        options.installerAssets.status !== "created" ||
        options.artifactChecksums.status !== "created" ||
        options.releaseManifest.status !== "created" ||
        options.releaseManifest.manifestPath === null ||
        options.installationReadme.status !== "written" ||
        options.installationReadme.path === null) {
        return createNotApplicableDownloadBundle();
    }
    const bundleDirectory = resolveReleaseDownloadBundleDirectory(options.state);
    const bundleDirectoryRelative = resolveReleaseDownloadBundleRelativeDirectory(options.state);
    if (bundleDirectory === null || bundleDirectoryRelative === null) {
        throw (0, errors_1.createUsageError)("Release download bundle requires a resolved download bundle directory.", [
            "Exit behavior: exits before user download bundle files are created."
        ]);
    }
    const bundledNoticeArtifacts = copyStagedNoticeFilesToDownloadBundle({
        state: options.state,
        bundleDirectory,
        bundleDirectoryRelative,
        noticeFiles: options.noticeFiles ?? []
    });
    const sourceArtifactPaths = [
        ...options.archivePayloads.targets.map((target) => target.archivePath),
        ...options.installerAssets.assets.map((asset) => asset.path),
        ...bundledNoticeArtifacts,
        options.artifactChecksums.checksumJsonPath,
        options.artifactChecksums.checksumTextPath,
        options.releaseManifest.manifestPath,
        options.installationReadme.path,
        `${bundleDirectoryRelative}/release-notes.md`
    ].filter((artifactPath) => typeof artifactPath === "string" && artifactPath.length > 0);
    const files = Array.from(new Set(sourceArtifactPaths))
        .map((artifactPath) => {
        const sourcePath = resolveArtifactPathInsideVersionDirectory({
            versionDirectory: options.state.versionDirectory,
            versionDirectoryRelative: options.state.versionDirectoryRelative,
            artifactPath,
            label: "download bundle source"
        });
        if (!node_fs_1.default.existsSync(sourcePath) || !node_fs_1.default.statSync(sourcePath).isFile()) {
            throw (0, errors_1.createUsageError)("Release download bundle is missing an expected user-facing artifact.", [
                "Reason: download-bundle-source-missing",
                `Artifact: ${artifactPath}`,
                "Recovery: regenerate the release download bundle after build artifacts are finalized.",
                "Exit behavior: exits before user download bundle files are reported."
            ]);
        }
        return artifactPath;
    })
        .sort();
    return {
        status: "created",
        directory: bundleDirectoryRelative,
        files
    };
}
function copyStagedNoticeFilesToDownloadBundle(options) {
    if (options.noticeFiles.length === 0) {
        return [];
    }
    if (options.state.stagingDirectory === null) {
        throw (0, errors_1.createUsageError)("Release download bundle notice copy requires a resolved staging directory.", [
            "Exit behavior: exits before user download bundle files are created."
        ]);
    }
    const noticeFiles = Array.from(new Set(options.noticeFiles)).sort();
    return noticeFiles.map((noticeFile) => {
        if (node_path_1.default.basename(noticeFile) !== noticeFile) {
            throw (0, errors_1.createUsageError)("Release notice file name is invalid for download bundle copy.", [
                "Reason: invalid-notice-file-name",
                `Notice file: ${noticeFile}`,
                "Recovery: keep notice file names as top-level file names without path separators.",
                "Exit behavior: exits before user download bundle files are created."
            ]);
        }
        const sourcePath = node_path_1.default.join(options.state.stagingDirectory, noticeFile);
        const destinationPath = node_path_1.default.join(options.bundleDirectory, noticeFile);
        if (!node_fs_1.default.existsSync(sourcePath) || !node_fs_1.default.statSync(sourcePath).isFile()) {
            throw (0, errors_1.createUsageError)("Release download bundle is missing a staged notice file.", [
                "Reason: download-bundle-notice-source-missing",
                `Notice file: ${noticeFile}`,
                "Recovery: regenerate release staging output before assembling download bundle.",
                "Exit behavior: exits before user download bundle files are reported."
            ]);
        }
        node_fs_1.default.copyFileSync(sourcePath, destinationPath);
        return `${options.bundleDirectoryRelative}/${noticeFile}`;
    });
}
function validateReleaseInstallationReadme(options) {
    const metadata = options.metadata ?? (options.manifest === undefined
        ? null
        : createReleaseInstallationReadmeMetadataFromManifest(options.manifest));
    if (metadata === null) {
        throw (0, errors_1.createUsageError)("Release installation README validation requires release metadata.", [
            "Reason: installation-readme-metadata-missing",
            "Recovery: regenerate README guidance from release metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    const requiredText = [
        `# Day Shift ${metadata.version} Installation`,
        metadata.version,
        metadata.command,
        metadata.requiredNodeVersion,
        metadata.installation.defaultInstallDirectory.posix,
        metadata.installation.defaultInstallDirectory.windows,
        metadata.installation.defaultBinDirectory.posix,
        metadata.installation.defaultBinDirectory.windows,
        `./bin/${metadata.command} --version`,
        `./bin/${metadata.command} about`,
        `${metadata.command} --version`,
        `${metadata.command} about`,
        "Extracted Archive",
        "POSIX Shell",
        "Windows PowerShell",
        "Beta and VIP signed license file",
        "signed offline Day Shift license file",
        "trusted Day Shift website/server-side path",
        `${metadata.command} license install <license-file>`,
        `${metadata.command} license status --format json`,
        "Troubleshooting",
        "Support handoff",
        "Upgrade or Reinstall",
        "Uninstall"
    ];
    for (const artifact of metadata.archives) {
        requiredText.push(artifact.filename, artifact.path);
        for (const supportedOs of artifact.supportedOs) {
            requiredText.push(supportedOs);
        }
    }
    const missing = requiredText.filter((token) => !options.markdown.includes(token));
    if (missing.length > 0) {
        throw (0, errors_1.createUsageError)("Release installation README is missing manifest-backed guidance.", [
            "Reason: installation-readme-metadata-drift",
            `Missing content: ${missing.join(", ")}`,
            "Recovery: regenerate README guidance from the release manifest and checksum metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    const forbiddenPatterns = [
        { label: "source checkout path", pattern: /\bsrc\/|\bdist\/cli\/index\.js\b|\.\/dist\b/ },
        { label: "public npm install", pattern: /\bnpm\s+install\b|\bnpx\b|\bpnpm\s+dlx\b/ },
        { label: "project repository write", pattern: /project repository|inside your repository|repo checkout/i },
        { label: "literal HOME path example", pattern: /'\$HOME[\\/]/ },
        { label: "public issuer command", pattern: /\bday-shift\s+license\s+issue\b/i },
        { label: "private issuer script", pattern: /\bissue-local-license\.js\b/i },
        { label: "private issuer helper", pattern: /\blicense-issuer\b/i },
        { label: "private operator directory", pattern: /\blocal-license-ops\b/i },
        { label: "private recipient input", pattern: /\brecipients\.(?:csv|json)\b/i },
        { label: "private generated license batch", pattern: /\blicenses-out\b|\b(?:beta|vip)[\w.-]*licenses?[\w.-]*\.(?:json|zip|tgz|tar\.gz)\b/i },
        { label: "private issuance report", pattern: /\bissuance-report\.json\b/i },
        { label: "private signing key", pattern: /\bprivate signing key\b|\blicense signing key\b|\bsigning-key\.(?:pem|key|json)\b/i },
        { label: "private support record", pattern: /\bsupport-records?\b|\bsupport-archive\.(?:zip|tgz|tar\.gz|enc|json)\b/i },
        { label: "email license delivery promise", pattern: /\bemail(?:ed)?\s+(?:JSON|license file|delivery|attachment)s?\b|\bsend\b[\s\S]{0,80}\blicense\b[\s\S]{0,80}\bemail\b/i },
        { label: "short-code redemption promise", pattern: /\bredeem(?: a)? short[- ]code\b|\bshort pasteable code\b/i },
        { label: "online activation promise", pattern: /\bonline activation required\b|\bactivate online\b/i },
        { label: "SSO activation promise", pattern: /\bSSO required\b|\blog in with SSO\b/i }
    ];
    const forbidden = forbiddenPatterns.filter((entry) => entry.pattern.test(options.markdown));
    if (forbidden.length > 0) {
        throw (0, errors_1.createUsageError)("Release installation README contains unsupported installation guidance.", [
            "Reason: installation-readme-forbidden-guidance",
            `Forbidden guidance: ${forbidden.map((entry) => entry.label).join(", ")}`,
            "Recovery: keep README examples focused on downloaded release artifacts and user-owned install locations.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
}
function validateReleaseManifest(options) {
    const manifest = normalizeReleaseManifestData(options.manifest);
    const parsedVersion = (0, version_1.parseReleaseVersion)(manifest.version);
    if (!parsedVersion.ok || parsedVersion.version !== options.expectedVersion) {
        throw (0, errors_1.createUsageError)("Release manifest contains malformed or inconsistent version metadata.", [
            "Reason: malformed-manifest-version",
            `Manifest version: ${manifest.version}`,
            `Expected version: ${options.expectedVersion}`,
            "Recovery: regenerate the manifest from explicit release version metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (manifest.command.installedName !== options.expectedInstalledCommandName) {
        throw (0, errors_1.createUsageError)("Release manifest command metadata is inconsistent.", [
            "Reason: manifest-command-name-mismatch",
            `Manifest command name: ${manifest.command.installedName}`,
            `Expected command name: ${options.expectedInstalledCommandName}`,
            "Recovery: regenerate the manifest from the configured release command name.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (normalizePayloadPath(manifest.command.stagedCommandPath) !== normalizePayloadPath(options.expectedStagedCommandPath)) {
        throw (0, errors_1.createUsageError)("Release manifest staged command metadata is inconsistent.", [
            "Reason: manifest-staged-command-path-mismatch",
            `Manifest staged command path: ${manifest.command.stagedCommandPath}`,
            `Expected staged command path: ${options.expectedStagedCommandPath}`,
            "Recovery: regenerate the manifest from the release command invocation mapping.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (manifest.status !== manifest.replacement.status) {
        throw (0, errors_1.createUsageError)("Release manifest status metadata is inconsistent.", [
            "Reason: manifest-status-mismatch",
            `Manifest status: ${manifest.status}`,
            `Replacement status: ${manifest.replacement.status}`,
            "Recovery: regenerate the manifest from release publication metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (manifest.command.invocations.development !== "node dist/cli/index.js" ||
        manifest.command.invocations.stagedRelease !== normalizePayloadPath(options.expectedStagedCommandPath) ||
        manifest.command.invocations.installedUser !== options.expectedInstalledCommandName) {
        throw (0, errors_1.createUsageError)("Release manifest command invocation metadata is inconsistent.", [
            "Reason: manifest-command-invocation-mismatch",
            `Development invocation: ${manifest.command.invocations.development}`,
            `Staged invocation: ${manifest.command.invocations.stagedRelease}`,
            `Installed invocation: ${manifest.command.invocations.installedUser}`,
            "Recovery: regenerate the manifest from the release command invocation mapping.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (manifest.runtime.model !== "system-node") {
        throw (0, errors_1.createUsageError)("Release manifest uses an unsupported runtime model.", [
            "Reason: unsupported-runtime-model",
            `Runtime model: ${String(manifest.runtime.model)}`,
            "Recovery: use system-node runtime metadata for MVP release artifacts.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    validateManifestWebsiteRelativePath(manifest.installation.readmePath, "installation README path");
    validateManifestWebsiteRelativePath(manifest.releaseNotes.path, "release notes path");
    validateManifestWebsiteDownloadUrl(manifest.releaseNotes.url, options.expectedVersion, "release-notes.md", "release notes URL");
    validateReleaseManifestSupportedPlatforms(manifest.supportedPlatforms);
    if (manifest.releaseNotes.sourceSummary.status !== "generated") {
        throw (0, errors_1.createUsageError)("Release manifest requires generated release notes evidence.", [
            "Reason: release-notes-not-generated",
            `Release notes: ${manifest.releaseNotes.path}`,
            `Status: ${manifest.releaseNotes.sourceSummary.status}`,
            "Recovery: generate release notes before publishing release manifest metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    const releaseNotesFilePath = resolveArtifactPathInsideVersionDirectory({
        versionDirectory: options.versionDirectory,
        versionDirectoryRelative: options.versionDirectoryRelative,
        artifactPath: manifest.releaseNotes.path,
        label: "release notes"
    });
    if (!node_fs_1.default.existsSync(releaseNotesFilePath) || !node_fs_1.default.statSync(releaseNotesFilePath).isFile()) {
        throw (0, errors_1.createUsageError)("Release manifest release notes file is missing.", [
            "Reason: missing-release-notes-file",
            `Release notes: ${manifest.releaseNotes.path}`,
            "Recovery: regenerate release notes before publishing release manifest metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    for (const artifact of manifest.artifacts) {
        const artifactPath = resolveArtifactPathInsideVersionDirectory({
            versionDirectory: options.versionDirectory,
            versionDirectoryRelative: options.versionDirectoryRelative,
            artifactPath: artifact.path,
            label: "manifest artifact"
        });
        if (!node_fs_1.default.existsSync(artifactPath) || !node_fs_1.default.statSync(artifactPath).isFile()) {
            throw (0, errors_1.createUsageError)("Release manifest artifact is missing.", [
                "Reason: missing-manifest-artifact-file",
                `Artifact: ${artifact.path}`,
                "Recovery: regenerate release archives before manifest generation.",
                "Exit behavior: exits before successful-release state is recorded."
            ]);
        }
        validateManifestWebsiteRelativePath(artifact.downloadPath, "artifact download path");
        validateManifestWebsiteDownloadPath(artifact.downloadPath, options.expectedVersion, artifact.filename, "artifact download path");
        validateManifestWebsiteDownloadUrl(artifact.url, options.expectedVersion, artifact.filename, "artifact URL");
        if (artifact.filename !== node_path_1.default.basename(artifactPath)) {
            throw (0, errors_1.createUsageError)("Release manifest artifact filename is inconsistent.", [
                "Reason: manifest-artifact-filename-mismatch",
                `Artifact: ${artifact.path}`,
                `Filename: ${artifact.filename}`,
                "Recovery: regenerate the manifest from checksum artifact records.",
                "Exit behavior: exits before successful-release state is recorded."
            ]);
        }
        if (artifact.runtimeModel !== "system-node") {
            throw (0, errors_1.createUsageError)("Release manifest artifact uses an unsupported runtime model.", [
                "Reason: unsupported-runtime-model",
                `Artifact: ${artifact.path}`,
                `Runtime model: ${String(artifact.runtimeModel)}`,
                "Recovery: use system-node runtime metadata for MVP release artifacts.",
                "Exit behavior: exits before successful-release state is recorded."
            ]);
        }
        const artifactBytes = node_fs_1.default.readFileSync(artifactPath);
        const actualSha256 = node_crypto_1.default.createHash("sha256").update(artifactBytes).digest("hex");
        if (artifact.byteSize !== artifactBytes.length) {
            throw (0, errors_1.createUsageError)("Release manifest artifact byte size does not match current file evidence.", [
                "Reason: artifact-byte-size-mismatch",
                `Artifact: ${artifact.path}`,
                `Expected byte size: ${artifact.byteSize}`,
                `Actual byte size: ${artifactBytes.length}`,
                "Recovery: regenerate checksum evidence and the release manifest after final artifact writes complete.",
                "Exit behavior: exits before successful-release state is recorded."
            ]);
        }
        if (artifact.sha256 !== actualSha256) {
            throw (0, errors_1.createUsageError)("Release manifest artifact SHA-256 does not match current file evidence.", [
                "Reason: artifact-checksum-mismatch",
                `Artifact: ${artifact.path}`,
                "Recovery: regenerate checksum evidence and the release manifest after final artifact writes complete.",
                "Exit behavior: exits before successful-release state is recorded."
            ]);
        }
    }
    const artifactsByPath = new Map(manifest.artifacts.map((artifact) => [artifact.path, artifact]));
    const missingManifestArtifacts = options.expectedArtifactPaths.filter((artifactPath) => !artifactsByPath.has(artifactPath));
    if (missingManifestArtifacts.length > 0) {
        throw (0, errors_1.createUsageError)("Release manifest is missing expected artifact metadata.", [
            "Reason: missing-manifest-artifact",
            `Missing artifacts: ${missingManifestArtifacts.join(", ")}`,
            "Recovery: regenerate the manifest after archive and checksum generation completes.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
}
function validateReleaseInstallerSafety(options) {
    const manifest = normalizeReleaseManifestData(options.manifest);
    const archiveArtifact = manifest.artifacts.find((artifact) => artifact.kind === "archive" &&
        artifact.target === options.target &&
        artifact.runtimeModel === "system-node");
    if (archiveArtifact === undefined) {
        throw (0, errors_1.createUsageError)("Release installer validation requires a matching system-node archive artifact.", [
            "Reason: installer-manifest-archive-missing",
            `Target: ${options.target}`,
            "Recovery: regenerate the manifest with archive metadata for the installer target.",
            "Exit behavior: exits before existing installation replacement."
        ]);
    }
    validateReleaseManifest({
        versionDirectory: options.versionDirectory,
        versionDirectoryRelative: options.versionDirectoryRelative,
        expectedVersion: options.expectedVersion,
        expectedInstalledCommandName: options.expectedInstalledCommandName,
        expectedStagedCommandPath: options.expectedStagedCommandPath,
        expectedArtifactPaths: [archiveArtifact.path],
        manifest
    });
    if (options.nodeVersion === null) {
        throw (0, errors_1.createUsageError)("Release installer validation requires system Node.js before installation changes.", [
            "Reason: missing-node",
            "Required Node.js version: >=20.0.0",
            "Recovery: install Node.js >=20.0.0 before running the installer.",
            "Exit behavior: exits before existing installation replacement."
        ]);
    }
    if (!isNodeVersionAtLeast20(options.nodeVersion)) {
        throw (0, errors_1.createUsageError)("Release installer validation requires Node.js >=20.0.0 before installation changes.", [
            "Reason: unsupported-node-version",
            `Node.js version: ${options.nodeVersion}`,
            "Required Node.js version: >=20.0.0",
            "Recovery: upgrade Node.js before running the installer.",
            "Exit behavior: exits before existing installation replacement."
        ]);
    }
    const payloadDirectory = node_path_1.default.resolve(options.payloadDirectory);
    if (!node_fs_1.default.existsSync(payloadDirectory) || !node_fs_1.default.statSync(payloadDirectory).isDirectory()) {
        throw (0, errors_1.createUsageError)("Release installer validation requires an unpacked archive payload directory.", [
            "Reason: installer-payload-missing",
            `Payload directory: ${normalizePayloadPath(options.payloadDirectory)}`,
            "Recovery: download, verify, and unpack the release archive before replacement.",
            "Exit behavior: exits before existing installation replacement."
        ]);
    }
    const installRoot = node_path_1.default.resolve(options.installRoot);
    const currentInstallDirectory = node_path_1.default.join(installRoot, "current");
    const stagedInstallDirectory = node_path_1.default.join(installRoot, ".day-shift-install-stage");
    const backupInstallDirectory = node_path_1.default.join(installRoot, ".day-shift-install-backup");
    const hadExistingInstall = node_fs_1.default.existsSync(currentInstallDirectory);
    const existingInstallSnapshot = hadExistingInstall
        ? snapshotDirectory(currentInstallDirectory)
        : [];
    node_fs_1.default.mkdirSync(installRoot, { recursive: true });
    node_fs_1.default.rmSync(stagedInstallDirectory, { recursive: true, force: true });
    node_fs_1.default.rmSync(backupInstallDirectory, { recursive: true, force: true });
    try {
        node_fs_1.default.cpSync(payloadDirectory, stagedInstallDirectory, { recursive: true });
        validateInstallerPayloadShims({
            target: options.target,
            installedCommandName: options.expectedInstalledCommandName,
            payloadDirectory: stagedInstallDirectory
        });
    }
    catch (error) {
        node_fs_1.default.rmSync(stagedInstallDirectory, { recursive: true, force: true });
        throw error;
    }
    try {
        if (hadExistingInstall) {
            node_fs_1.default.renameSync(currentInstallDirectory, backupInstallDirectory);
        }
        if (options.simulateReplacementFailure === true) {
            throw (0, errors_1.createUsageError)("Release installer staged replacement failed.", [
                "Reason: installer-replacement-failed",
                `Install root: ${normalizePayloadPath(options.installRoot)}`,
                "Recovery: keep the existing installation and retry after fixing filesystem permissions or locks.",
                "Exit behavior: restores the existing installation before returning failure."
            ]);
        }
        node_fs_1.default.renameSync(stagedInstallDirectory, currentInstallDirectory);
        node_fs_1.default.rmSync(backupInstallDirectory, { recursive: true, force: true });
    }
    catch (error) {
        node_fs_1.default.rmSync(currentInstallDirectory, { recursive: true, force: true });
        if (node_fs_1.default.existsSync(backupInstallDirectory)) {
            node_fs_1.default.renameSync(backupInstallDirectory, currentInstallDirectory);
        }
        node_fs_1.default.rmSync(stagedInstallDirectory, { recursive: true, force: true });
        if (isUsageErrorLike(error)) {
            throw error;
        }
        throw (0, errors_1.createUsageError)("Release installer staged replacement failed.", [
            "Reason: installer-replacement-failed",
            `Install root: ${normalizePayloadPath(options.installRoot)}`,
            "Recovery: keep the existing installation and retry after fixing filesystem permissions or locks.",
            "Exit behavior: restores the existing installation before returning failure."
        ]);
    }
    return {
        status: "passed",
        target: options.target,
        installRoot: normalizePayloadPath(options.installRoot),
        currentInstallDirectory: normalizePayloadPath(currentInstallDirectory),
        stagedInstallDirectory: normalizePayloadPath(stagedInstallDirectory),
        existingInstallPreserved: hadExistingInstall
            ? directoriesMatchSnapshot(currentInstallDirectory, existingInstallSnapshot) === false
            : true,
        installedFiles: collectFiles(currentInstallDirectory)
            .map((filePath) => toRepositoryRelativePath(currentInstallDirectory, filePath))
            .sort()
    };
}
function parseRetentionKeep(value) {
    const raw = typeof value === "string" && value.trim().length > 0
        ? value.trim()
        : "3";
    const keep = Number(raw);
    if (!Number.isInteger(keep) || keep < 0) {
        throw (0, errors_1.createUsageError)(`Invalid release retention keep count: ${raw}`, [
            "Reason: invalid-retention-keep-count",
            "Recovery: provide a non-negative integer, for example --keep 3.",
            "Exit behavior: exits before local generated release output is pruned."
        ]);
    }
    return keep;
}
function applyReleaseRetention(options) {
    if (options.state.versionDirectoryRelative === null) {
        throw (0, errors_1.createUsageError)("Release retention requires a resolved current version directory.", [
            "Exit behavior: exits before local generated release output is pruned."
        ]);
    }
    const entries = listReleaseDirectoryEntries(options.state);
    const successfulPriorEntries = entries
        .filter((entry) => entry.name !== options.currentVersion)
        .filter((entry) => entry.marker.valid && entry.marker.version === entry.name)
        .sort((left, right) => compareStableVersionsDescending(left.name, right.name));
    const retainedPriorPaths = new Set(successfulPriorEntries
        .slice(0, options.keepSuccessfulPriorCount)
        .map((entry) => entry.relativePath));
    const retainedPaths = [
        options.state.versionDirectoryRelative,
        ...Array.from(retainedPriorPaths)
    ];
    const removedEntries = entries.filter((entry) => {
        if (entry.name === options.currentVersion) {
            return false;
        }
        return !retainedPriorPaths.has(entry.relativePath);
    });
    if (!options.dryRun) {
        for (const entry of removedEntries) {
            node_fs_1.default.rmSync(entry.path, { recursive: true, force: true });
        }
    }
    return {
        kind: "local-retention",
        dryRun: options.dryRun,
        changed: !options.dryRun && removedEntries.length > 0,
        removedPaths: removedEntries.map((entry) => entry.relativePath),
        retainedPaths,
        message: options.dryRun
            ? "Dry run reported local generated release directories that retention would remove."
            : "Applied local generated release directory retention."
    };
}
function applyReleaseCleanup(options) {
    if (!options.dryRun && !options.confirm) {
        throw (0, errors_1.createUsageError)("Release cleanup requires --confirm before deleting local generated release output.", [
            "Reason: cleanup-confirmation-required",
            "Recovery: rerun with --confirm, or use --dry-run to inspect the local-only cleanup target.",
            "Exit behavior: exits before local generated release output is deleted."
        ]);
    }
    const exists = node_fs_1.default.existsSync(options.state.releaseRoot);
    if (!options.dryRun && exists) {
        node_fs_1.default.rmSync(options.state.releaseRoot, { recursive: true, force: true });
    }
    return {
        kind: "local-cleanup",
        dryRun: options.dryRun,
        changed: !options.dryRun && exists,
        removedPaths: exists ? [options.state.releaseRootRelative] : [],
        retainedPaths: [],
        message: options.dryRun
            ? "Dry run reported the local generated release output root that cleanup would remove."
            : "Removed local generated release output only; published immutable artifacts were not touched."
    };
}
function resolvePathWithinRepository(options) {
    const raw = typeof options.value === "string" && options.value.trim().length > 0
        ? options.value.trim()
        : options.fallback;
    const resolved = node_path_1.default.resolve(options.repositoryRoot, raw);
    const relative = node_path_1.default.relative(options.repositoryRoot, resolved);
    if (relative === ".." || relative.startsWith(`..${node_path_1.default.sep}`) || node_path_1.default.isAbsolute(relative)) {
        throw (0, errors_1.createUsageError)(`Release ${options.label} must stay inside the repository: ${raw}`, [
            "Reason: output-root-outside-repository",
            "Recovery: choose a repository-relative generated output directory such as release.",
            "Exit behavior: exits before generated release output directories or artifacts are created."
        ]);
    }
    return resolved;
}
function resolveReleaseDownloadBundleDirectory(state) {
    return state.versionDirectory === null
        ? null
        : node_path_1.default.join(state.versionDirectory, exports.RELEASE_DOWNLOAD_BUNDLE_DIRECTORY);
}
function resolveReleaseDownloadBundleRelativeDirectory(state) {
    return state.versionDirectoryRelative === null
        ? null
        : `${state.versionDirectoryRelative}/${exports.RELEASE_DOWNLOAD_BUNDLE_DIRECTORY}`;
}
function resolveRequiredDownloadBundleRelativeDirectory(state) {
    const bundleDirectory = resolveReleaseDownloadBundleRelativeDirectory(state);
    if (bundleDirectory === null) {
        throw (0, errors_1.createUsageError)("Release output requires a resolved download bundle directory.", [
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    return bundleDirectory;
}
function createRuntimePackageJson(options) {
    const runtimePackageJson = {};
    const copiedFields = [
        "name",
        "private",
        "description",
        "license",
        "type",
        "main",
        "engines",
        "dependencies"
    ];
    for (const field of copiedFields) {
        if (Object.prototype.hasOwnProperty.call(options.packageJson, field)) {
            runtimePackageJson[field] = options.packageJson[field];
        }
    }
    runtimePackageJson.version = options.version;
    runtimePackageJson.bin = {
        [options.installedCommandName]: "dist/cli/index.js"
    };
    return runtimePackageJson;
}
function writeReleaseLaunchers(options) {
    const binDirectory = node_path_1.default.join(options.stagingDirectory, "bin");
    node_fs_1.default.mkdirSync(binDirectory, { recursive: true });
    const posixLauncherPath = node_path_1.default.join(binDirectory, options.installedCommandName);
    node_fs_1.default.writeFileSync(posixLauncherPath, [
        "#!/usr/bin/env sh",
        "set -eu",
        "SCRIPT_PATH=$0",
        "while [ -L \"$SCRIPT_PATH\" ]; do",
        "  SCRIPT_DIR=$(CDPATH= cd -- \"$(dirname -- \"$SCRIPT_PATH\")\" && pwd)",
        "  LINK_TARGET=$(readlink \"$SCRIPT_PATH\")",
        "  case \"$LINK_TARGET\" in",
        "    /*) SCRIPT_PATH=$LINK_TARGET ;;",
        "    *) SCRIPT_PATH=$SCRIPT_DIR/$LINK_TARGET ;;",
        "  esac",
        "done",
        "SCRIPT_DIR=$(CDPATH= cd -- \"$(dirname -- \"$SCRIPT_PATH\")\" && pwd)",
        "INSTALL_ROOT=$(CDPATH= cd -- \"$SCRIPT_DIR/..\" && pwd)",
        "exec node \"$INSTALL_ROOT/dist/cli/index.js\" \"$@\"",
        ""
    ].join("\n"));
    node_fs_1.default.chmodSync(posixLauncherPath, 0o755);
    const cmdLauncherPath = node_path_1.default.join(binDirectory, `${options.installedCommandName}.cmd`);
    node_fs_1.default.writeFileSync(cmdLauncherPath, [
        "@echo off",
        "setlocal",
        "set \"SCRIPT_DIR=%~dp0\"",
        "node \"%SCRIPT_DIR%..\\dist\\cli\\index.js\" %*",
        "exit /b %ERRORLEVEL%",
        ""
    ].join("\r\n"));
    const powershellLauncherPath = node_path_1.default.join(binDirectory, `${options.installedCommandName}.ps1`);
    node_fs_1.default.writeFileSync(powershellLauncherPath, [
        "$ScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path",
        "$InstallRoot = Split-Path -Parent $ScriptRoot",
        "$Entrypoint = Join-Path $InstallRoot 'dist\\cli\\index.js'",
        "& node $Entrypoint @args",
        "exit $LASTEXITCODE",
        ""
    ].join("\r\n"));
    return [
        {
            platform: "posix",
            path: `${options.stagingDirectoryRelative}/bin/${options.installedCommandName}`,
            lineEndings: "lf",
            executable: isExecutable(posixLauncherPath)
        },
        {
            platform: "windows",
            path: `${options.stagingDirectoryRelative}/bin/${options.installedCommandName}.cmd`,
            lineEndings: "crlf",
            executable: false
        },
        {
            platform: "windows",
            path: `${options.stagingDirectoryRelative}/bin/${options.installedCommandName}.ps1`,
            lineEndings: "crlf",
            executable: false
        }
    ];
}
function normalizeArchiveNamePart(value) {
    const normalized = value
        .trim()
        .toLowerCase()
        .replace(/^@/, "")
        .replace(/[\\/]+/g, "-")
        .replace(/[^a-z0-9._-]+/g, "-")
        .replace(/^-+|-+$/g, "");
    return normalized.length === 0 ? "day-shift" : normalized;
}
function isExecutable(filePath) {
    return (node_fs_1.default.statSync(filePath).mode & 0o111) !== 0;
}
function removeIfExists(filePath) {
    node_fs_1.default.rmSync(filePath, { force: true });
}
function createChecksumRecord(options) {
    const normalizedArtifactPath = normalizePayloadPath(options.artifactPath);
    const resolvedArtifactPath = resolveArtifactPathInsideVersionDirectory({
        versionDirectory: options.versionDirectory,
        versionDirectoryRelative: options.versionDirectoryRelative,
        artifactPath: options.artifactPath,
        label: "artifact"
    });
    const relativePath = node_path_1.default.relative(options.versionDirectory, resolvedArtifactPath);
    const relativeArtifactPath = `${options.versionDirectoryRelative}/${relativePath.split(node_path_1.default.sep).join("/")}`;
    if (!node_fs_1.default.existsSync(resolvedArtifactPath) || !node_fs_1.default.statSync(resolvedArtifactPath).isFile()) {
        throw (0, errors_1.createUsageError)("Release artifact checksum input is missing.", [
            "Reason: missing-artifact",
            `Artifact path: ${normalizedArtifactPath}`,
            "Recovery: generate release archives or installer assets before checksum generation.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    const fileContents = node_fs_1.default.readFileSync(resolvedArtifactPath);
    return {
        kind: options.kind,
        target: options.target,
        filename: node_path_1.default.basename(resolvedArtifactPath),
        path: relativeArtifactPath,
        byteSize: fileContents.length,
        sha256: node_crypto_1.default.createHash("sha256").update(fileContents).digest("hex")
    };
}
function resolveArtifactPathInsideVersionDirectory(options) {
    const normalizedArtifactPath = normalizePayloadPath(options.artifactPath);
    const versionPrefix = `${options.versionDirectoryRelative}/`;
    const resolvedArtifactPath = node_path_1.default.isAbsolute(options.artifactPath)
        ? node_path_1.default.resolve(options.artifactPath)
        : normalizedArtifactPath.startsWith(versionPrefix)
            ? node_path_1.default.join(options.versionDirectory, ...normalizedArtifactPath.slice(versionPrefix.length).split("/"))
            : node_path_1.default.resolve(options.versionDirectory, options.artifactPath);
    const relativePath = node_path_1.default.relative(options.versionDirectory, resolvedArtifactPath);
    if (relativePath === ".." || relativePath.startsWith(`..${node_path_1.default.sep}`) || node_path_1.default.isAbsolute(relativePath)) {
        throw (0, errors_1.createUsageError)(`Release ${options.label} path must stay inside the versioned release directory.`, [
            `Reason: ${options.label}-path-outside-version-directory`,
            `${options.label[0].toUpperCase()}${options.label.slice(1)} path: ${options.artifactPath}`,
            "Recovery: keep archive, installer, and checksum artifacts under the resolved release/<version>/ directory.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    return resolvedArtifactPath;
}
function normalizeChecksumRecord(value) {
    if (typeof value !== "object" || value === null || Array.isArray(value)) {
        throw (0, errors_1.createUsageError)("Release artifact checksum record contains an invalid artifact entry.", [
            "Reason: malformed-checksum-artifact-record",
            "Recovery: regenerate checksum evidence with the release checksum generator.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    const record = value;
    const kind = record.kind;
    const target = record.target;
    const filename = record.filename;
    const artifactPath = record.path;
    const byteSize = record.byteSize;
    const sha256 = record.sha256;
    if ((kind !== "archive" && kind !== "installer") ||
        !["macos-linux", "windows", "posix-installer", "windows-installer"].includes(String(target)) ||
        typeof filename !== "string" ||
        typeof artifactPath !== "string" ||
        typeof byteSize !== "number" ||
        !Number.isSafeInteger(byteSize) ||
        byteSize < 0 ||
        typeof sha256 !== "string" ||
        !/^[a-f0-9]{64}$/.test(sha256)) {
        throw (0, errors_1.createUsageError)("Release artifact checksum record contains invalid artifact metadata.", [
            "Reason: malformed-checksum-artifact-record",
            "Recovery: regenerate checksum evidence with the release checksum generator.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    return {
        kind,
        target: target,
        filename,
        path: artifactPath,
        byteSize,
        sha256
    };
}
function normalizeReleaseManifestData(value) {
    if (typeof value !== "object" || value === null || Array.isArray(value)) {
        throw (0, errors_1.createUsageError)("Release manifest contains invalid top-level metadata.", [
            "Reason: malformed-release-manifest",
            "Recovery: regenerate the manifest from release artifact evidence.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    const manifest = value;
    if (!["active", "deprecated", "withdrawn", "replaced"].includes(String(manifest.status))) {
        throw (0, errors_1.createUsageError)("Release manifest status metadata is missing or malformed.", [
            "Reason: malformed-manifest-status",
            "Recovery: regenerate the manifest from release publication metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (!isRecord(manifest.command)) {
        throw (0, errors_1.createUsageError)("Release manifest command metadata is missing or malformed.", [
            "Reason: malformed-manifest-command",
            "Recovery: regenerate the manifest from the configured release command name and staged command path.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (typeof manifest.command.installedName !== "string" ||
        manifest.command.installedName.trim().length === 0 ||
        typeof manifest.command.stagedCommandPath !== "string" ||
        manifest.command.stagedCommandPath.trim().length === 0 ||
        !isRecord(manifest.command.invocations) ||
        typeof manifest.command.invocations.development !== "string" ||
        manifest.command.invocations.development.trim().length === 0 ||
        typeof manifest.command.invocations.stagedRelease !== "string" ||
        manifest.command.invocations.stagedRelease.trim().length === 0 ||
        typeof manifest.command.invocations.installedUser !== "string" ||
        manifest.command.invocations.installedUser.trim().length === 0) {
        throw (0, errors_1.createUsageError)("Release manifest command metadata is missing required fields.", [
            "Reason: malformed-manifest-command",
            "Recovery: regenerate the manifest from the configured release command name and staged command path.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (!isRecord(manifest.runtime)) {
        throw (0, errors_1.createUsageError)("Release manifest runtime metadata is missing or malformed.", [
            "Reason: malformed-manifest-runtime",
            "Recovery: regenerate the manifest from release runtime metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (typeof manifest.runtime.model !== "string" ||
        typeof manifest.runtime.requiredNodeVersion !== "string" ||
        manifest.runtime.requiredNodeVersion.trim().length === 0 ||
        typeof manifest.runtime.productionDependenciesIncluded !== "boolean") {
        throw (0, errors_1.createUsageError)("Release manifest runtime metadata is missing required fields.", [
            "Reason: malformed-manifest-runtime",
            "Recovery: regenerate the manifest from release runtime metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (!isRecord(manifest.installation)) {
        throw (0, errors_1.createUsageError)("Release manifest installation metadata is missing or malformed.", [
            "Reason: malformed-manifest-installation",
            "Recovery: regenerate the manifest from release installation metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (typeof manifest.installation.readmePath !== "string" ||
        !isRecord(manifest.installation.defaultInstallDirectory) ||
        typeof manifest.installation.defaultInstallDirectory.posix !== "string" ||
        typeof manifest.installation.defaultInstallDirectory.windows !== "string" ||
        !isRecord(manifest.installation.defaultBinDirectory) ||
        typeof manifest.installation.defaultBinDirectory.posix !== "string" ||
        typeof manifest.installation.defaultBinDirectory.windows !== "string") {
        throw (0, errors_1.createUsageError)("Release manifest installation metadata is missing required fields.", [
            "Reason: malformed-manifest-installation",
            "Recovery: regenerate the manifest from release installation metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (!Array.isArray(manifest.artifacts)) {
        throw (0, errors_1.createUsageError)("Release manifest artifact metadata is missing or malformed.", [
            "Reason: malformed-manifest-artifact",
            "Recovery: regenerate the manifest from checksum artifact records.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    const compatibility = normalizeReleaseManifestCompatibility(manifest.compatibility);
    const supportedPlatforms = normalizeReleaseManifestSupportedPlatforms(manifest.supportedPlatforms);
    if (!isRecord(manifest.replacement) ||
        !["active", "deprecated", "withdrawn", "replaced"].includes(String(manifest.replacement.status))) {
        throw (0, errors_1.createUsageError)("Release manifest replacement metadata is missing or malformed.", [
            "Reason: malformed-manifest-replacement",
            "Recovery: regenerate the manifest from release replacement metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (!isRecord(manifest.releaseNotes) ||
        typeof manifest.releaseNotes.path !== "string" ||
        typeof manifest.releaseNotes.url !== "string") {
        throw (0, errors_1.createUsageError)("Release manifest release-notes metadata is missing or malformed.", [
            "Reason: malformed-manifest-release-notes",
            "Recovery: regenerate the manifest from release notes evidence.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    normalizeReleaseNotesSourceSummary(manifest.releaseNotes.sourceSummary);
    return {
        ...manifest,
        status: manifest.status,
        command: {
            installedName: manifest.command.installedName,
            stagedCommandPath: manifest.command.stagedCommandPath,
            invocations: {
                development: manifest.command.invocations.development,
                stagedRelease: manifest.command.invocations.stagedRelease,
                installedUser: manifest.command.invocations.installedUser
            }
        },
        runtime: {
            model: manifest.runtime.model,
            requiredNodeVersion: manifest.runtime.requiredNodeVersion,
            productionDependenciesIncluded: manifest.runtime.productionDependenciesIncluded
        },
        supportedPlatforms,
        installation: {
            readmePath: manifest.installation.readmePath,
            defaultInstallDirectory: {
                posix: manifest.installation.defaultInstallDirectory.posix,
                windows: manifest.installation.defaultInstallDirectory.windows
            },
            defaultBinDirectory: {
                posix: manifest.installation.defaultBinDirectory.posix,
                windows: manifest.installation.defaultBinDirectory.windows
            }
        },
        compatibility,
        artifacts: manifest.artifacts.map((artifact) => normalizeManifestArtifactRecord(artifact)),
        releaseNotes: {
            ...manifest.releaseNotes,
            path: manifest.releaseNotes.path,
            url: manifest.releaseNotes.url
        }
    };
}
function normalizeReleaseNotesSourceSummary(value) {
    if (!isRecord(value) || typeof value.previousReleaseMarker !== "string" || typeof value.planningArtifactRange !== "string") {
        throw (0, errors_1.createUsageError)("Release manifest release-notes source summary is missing required marker metadata.", [
            "Reason: malformed-release-notes-source-summary",
            "Recovery: regenerate release notes source metadata before manifest validation.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (value.status !== "generated" && value.status !== "not-generated") {
        throw (0, errors_1.createUsageError)("Release manifest release-notes source summary has an invalid status.", [
            "Reason: malformed-release-notes-source-summary",
            "Recovery: regenerate release notes source metadata before manifest validation.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (!isRecord(value.marker) || !isRecord(value.counts) || !Array.isArray(value.selectedSources) || !Array.isArray(value.excludedSources)) {
        throw (0, errors_1.createUsageError)("Release manifest release-notes source summary has malformed trace metadata.", [
            "Reason: malformed-release-notes-source-summary",
            "Recovery: regenerate release notes source metadata before manifest validation.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    return value;
}
function normalizeReleaseManifestCompatibility(value) {
    if (!isRecord(value)) {
        throw (0, errors_1.createUsageError)("Release manifest compatibility metadata is missing or malformed.", [
            "Reason: malformed-manifest-compatibility",
            "Recovery: regenerate the manifest from release compatibility metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (!isRecord(value.artifactSchemas)) {
        throw (0, errors_1.createUsageError)("Release manifest compatibility artifact schema metadata is missing or malformed.", [
            "Reason: malformed-manifest-compatibility",
            "Recovery: regenerate the manifest from release compatibility metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    return {
        workspaceMetadata: normalizeCompatibilityVersionRange(value.workspaceMetadata, "workspaceMetadata"),
        artifactSchemas: {
            specification: normalizeCompatibilityVersionRange(value.artifactSchemas.specification, "artifactSchemas.specification"),
            planning: normalizeCompatibilityVersionRange(value.artifactSchemas.planning, "artifactSchemas.planning"),
            prompt: normalizeCompatibilityVersionRange(value.artifactSchemas.prompt, "artifactSchemas.prompt"),
            template: normalizeCompatibilityVersionRange(value.artifactSchemas.template, "artifactSchemas.template"),
            promptManifest: normalizeCompatibilityVersionRange(value.artifactSchemas.promptManifest, "artifactSchemas.promptManifest"),
            templateManifest: normalizeCompatibilityVersionRange(value.artifactSchemas.templateManifest, "artifactSchemas.templateManifest"),
            releaseManifest: normalizeCompatibilityVersionRange(value.artifactSchemas.releaseManifest, "artifactSchemas.releaseManifest"),
            releaseRuntimeMetadata: normalizeCompatibilityVersionRange(value.artifactSchemas.releaseRuntimeMetadata, "artifactSchemas.releaseRuntimeMetadata")
        }
    };
}
function normalizeCompatibilityVersionRange(value, label) {
    const parsed = (0, workspace_compatibility_1.readCompatibilityVersionRangeShape)(value);
    if (!parsed.ok && parsed.reason === "missing") {
        throw (0, errors_1.createUsageError)("Release manifest compatibility range is missing or malformed.", [
            "Reason: malformed-manifest-compatibility",
            `Range: ${label}`,
            "Recovery: regenerate the manifest from release compatibility metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (!parsed.ok && parsed.reason === "malformed") {
        throw (0, errors_1.createUsageError)("Release manifest compatibility range must use positive integer bounds.", [
            "Reason: malformed-manifest-compatibility",
            `Range: ${label}`,
            "Recovery: regenerate the manifest from release compatibility metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (!parsed.ok && parsed.reason === "contradictory") {
        const record = value;
        throw (0, errors_1.createUsageError)("Release manifest compatibility range is contradictory.", [
            "Reason: contradictory-manifest-compatibility",
            `Range: ${label}`,
            `Minimum: ${String(record.minimum)}`,
            `Maximum: ${String(record.maximum)}`,
            "Recovery: regenerate the manifest with a minimum version less than or equal to the maximum version.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    if (!parsed.ok) {
        throw (0, errors_1.createUsageError)("Release manifest compatibility range is missing or malformed.", [
            "Reason: malformed-manifest-compatibility",
            `Range: ${label}`,
            "Recovery: regenerate the manifest from release compatibility metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    return parsed.range;
}
function normalizeManifestArtifactRecord(value) {
    if (typeof value !== "object" || value === null || Array.isArray(value)) {
        throw (0, errors_1.createUsageError)("Release manifest contains an invalid artifact entry.", [
            "Reason: malformed-manifest-artifact",
            "Recovery: regenerate the manifest from checksum artifact records.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    const artifact = value;
    const kind = artifact.kind;
    const target = artifact.target;
    const filename = artifact.filename;
    const artifactPath = artifact.path;
    const downloadPath = artifact.downloadPath;
    const url = artifact.url;
    const byteSize = artifact.byteSize;
    const sha256 = artifact.sha256;
    const runtimeModel = artifact.runtimeModel;
    if ((kind !== "archive" && kind !== "installer") ||
        !["macos-linux", "windows", "posix-installer", "windows-installer"].includes(String(target)) ||
        typeof filename !== "string" ||
        filename.trim().length === 0 ||
        typeof artifactPath !== "string" ||
        artifactPath.trim().length === 0 ||
        typeof downloadPath !== "string" ||
        downloadPath.trim().length === 0 ||
        typeof url !== "string" ||
        url.trim().length === 0 ||
        typeof byteSize !== "number" ||
        !Number.isSafeInteger(byteSize) ||
        byteSize < 0 ||
        typeof sha256 !== "string" ||
        !/^[a-f0-9]{64}$/.test(sha256) ||
        typeof runtimeModel !== "string") {
        throw (0, errors_1.createUsageError)("Release manifest artifact metadata is missing or malformed.", [
            "Reason: malformed-manifest-artifact",
            "Recovery: regenerate the manifest from checksum artifact records.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    return {
        ...artifact,
        kind,
        target: target,
        filename,
        path: artifactPath,
        downloadPath,
        url,
        byteSize,
        sha256,
        runtimeModel: runtimeModel
    };
}
function isRecord(value) {
    return typeof value === "object" && value !== null && !Array.isArray(value);
}
function collectInstallerAssetPaths(versionDirectory) {
    const bundleDirectory = node_path_1.default.join(versionDirectory, exports.RELEASE_DOWNLOAD_BUNDLE_DIRECTORY);
    if (!node_fs_1.default.existsSync(bundleDirectory)) {
        return [];
    }
    return node_fs_1.default.readdirSync(bundleDirectory, { withFileTypes: true })
        .filter((entry) => entry.isFile())
        .map((entry) => node_path_1.default.join(bundleDirectory, entry.name))
        .filter((assetPath) => isInstallerAssetFilename(node_path_1.default.basename(assetPath)))
        .sort();
}
function isInstallerAssetFilename(filename) {
    return /^install(?:-[a-z0-9._-]+)?\.(?:sh|ps1|cmd)$/i.test(filename) ||
        /^uninstall(?:-[a-z0-9._-]+)?\.(?:sh|ps1|cmd)$/i.test(filename) ||
        /^day-shift-installer(?:-[a-z0-9._-]+)?\.(?:sh|ps1|cmd)$/i.test(filename);
}
function classifyInstallerAssetTarget(filename) {
    return /\.(?:ps1|cmd)$/i.test(filename) ? "windows-installer" : "posix-installer";
}
function normalizeDownloadOrigin(value) {
    const trimmed = value.trim();
    if (!/^https:\/\/[^/\s]+(?:\/[^\s]*)?$/i.test(trimmed)) {
        throw (0, errors_1.createUsageError)("Release installer official origin must be an HTTPS URL.", [
            "Reason: invalid-installer-origin",
            "Recovery: provide an HTTPS website distribution origin such as https://tnsds.tech/downloads/.",
            "Exit behavior: exits before generated installer assets are created."
        ]);
    }
    return trimmed.endsWith("/") ? trimmed : `${trimmed}/`;
}
function renderPosixInstallerAsset(options) {
    return [
        "#!/usr/bin/env sh",
        "set -eu",
        "",
        `DAY_SHIFT_DEFAULT_VERSION=${shellQuote(options.defaultVersion)}`,
        `DAY_SHIFT_COMMAND_NAME=${shellQuote(options.installedCommandName)}`,
        `DAY_SHIFT_ORIGIN=${shellQuote(options.officialOrigin)}`,
        "DAY_SHIFT_VERSION=${DAY_SHIFT_VERSION:-$DAY_SHIFT_DEFAULT_VERSION}",
        "DAY_SHIFT_INSTALL_DIR=${DAY_SHIFT_INSTALL_DIR:-$HOME/.local/day-shift}",
        "DAY_SHIFT_BIN_DIR=${DAY_SHIFT_BIN_DIR:-$HOME/.local/bin}",
        "",
        "fail() {",
        "  printf '%s\\n' \"$1\" >&2",
        "  exit 1",
        "}",
        "",
        "need_command() {",
        "  command -v \"$1\" >/dev/null 2>&1 || fail \"Missing required command: $1\"",
        "}",
        "",
        "version_ge_20() {",
        "  node_version=$(node --version 2>/dev/null | sed 's/^v//')",
        "  node_major=${node_version%%.*}",
        "  case \"$node_major\" in",
        "    ''|*[!0-9]*) return 1 ;;",
        "  esac",
        "  [ \"$node_major\" -ge 20 ]",
        "}",
        "",
        "need_command node",
        "need_command tar",
        "need_command shasum",
        "version_ge_20 || fail 'Day Shift requires system Node.js >=20.0.0 before installation changes are made.'",
        "",
        "resolve_script_dir() {",
        "  script_path=$0",
        "  while [ -L \"$script_path\" ]; do",
        "    script_dir=$(CDPATH= cd -- \"$(dirname -- \"$script_path\")\" && pwd)",
        "    link_target=$(readlink \"$script_path\")",
        "    case \"$link_target\" in",
        "      /*) script_path=$link_target ;;",
        "      *) script_path=$script_dir/$link_target ;;",
        "    esac",
        "  done",
        "  CDPATH= cd -- \"$(dirname -- \"$script_path\")\" && pwd",
        "}",
        "",
        "validate_manifest_json() {",
        "  manifest_candidate_path=$1",
        "  manifest_candidate_source=$2",
        "  node -e \"const fs=require('fs'); const manifestPath=process.argv[1]; const manifestSource=process.argv[2]; let text=''; try { text=fs.readFileSync(manifestPath,'utf8'); } catch (error) { console.error('Failed to read downloaded release manifest.'); process.exit(1); } try { const parsed=JSON.parse(text); if(!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('shape'); } catch (error) { const trimmed=text.trimStart(); const likelyHtml=/^<!doctype html[>\\s]/i.test(trimmed) || /^<html[>\\s]/i.test(trimmed); const reason=likelyHtml ? 'The download returned HTML instead of JSON.' : 'The download did not contain valid JSON.'; console.error('Downloaded release manifest from ' + manifestSource + ' could not be parsed. ' + reason); console.error('Check that DAY_SHIFT_ORIGIN points at the published downloads directory and that release-manifest.json is reachable there.'); process.exit(1); }\" \"$manifest_candidate_path\" \"$manifest_candidate_source\" || fail 'Downloaded release manifest is invalid.'",
        "}",
        "",
        "work_dir=$(mktemp -d 2>/dev/null || mktemp -d -t day-shift-install)",
        "cleanup() { rm -rf \"$work_dir\"; }",
        "trap cleanup EXIT INT TERM",
        "",
        "script_dir=$(resolve_script_dir)",
        "local_manifest_path=\"$script_dir/release-manifest.json\"",
        "manifest_path=\"$work_dir/release-manifest.json\"",
        "manifest_source=''",
        "use_local_release_assets=false",
        "if [ -f \"$local_manifest_path\" ]; then",
        "  manifest_path=\"$local_manifest_path\"",
        "  manifest_source=\"$local_manifest_path\"",
        "  use_local_release_assets=true",
        "else",
        "  need_command curl",
        "  manifest_url=\"${DAY_SHIFT_ORIGIN%/}/release-manifest.json\"",
        "  if [ \"$DAY_SHIFT_VERSION\" != latest ]; then",
        "    manifest_url=\"${DAY_SHIFT_ORIGIN%/}/$DAY_SHIFT_VERSION/release-manifest.json\"",
        "  fi",
        "  curl -fsSL \"$manifest_url\" -o \"$manifest_path\"",
        "  manifest_source=\"$manifest_url\"",
        "fi",
        "validate_manifest_json \"$manifest_path\" \"$manifest_source\"",
        "",
        "resolved_version=$(node -e \"const fs=require('fs'); const m=JSON.parse(fs.readFileSync(process.argv[1],'utf8')); if(!m.version || m.version === 'latest') process.exit(2); process.stdout.write(m.version);\" \"$manifest_path\")",
        "archive_filename=$(node -e \"const fs=require('fs'); const m=JSON.parse(fs.readFileSync(process.argv[1],'utf8')); const a=(m.artifacts||[]).find((item)=>item.target==='macos-linux' && item.kind==='archive' && item.runtimeModel==='system-node'); if(!a) process.exit(2); process.stdout.write(a.filename);\" \"$manifest_path\")",
        "archive_sha256=$(node -e \"const fs=require('fs'); const m=JSON.parse(fs.readFileSync(process.argv[1],'utf8')); const a=(m.artifacts||[]).find((item)=>item.target==='macos-linux' && item.kind==='archive' && item.runtimeModel==='system-node'); if(!a || !/^[a-f0-9]{64}$/.test(a.sha256)) process.exit(2); process.stdout.write(a.sha256);\" \"$manifest_path\")",
        "manifest_command=$(node -e \"const fs=require('fs'); const m=JSON.parse(fs.readFileSync(process.argv[1],'utf8')); if(!m.command || !m.command.installedName) process.exit(2); process.stdout.write(m.command.installedName);\" \"$manifest_path\")",
        "[ \"$manifest_command\" = \"$DAY_SHIFT_COMMAND_NAME\" ] || DAY_SHIFT_COMMAND_NAME=$manifest_command",
        "",
        "archive_path=\"$work_dir/$archive_filename\"",
        "extract_dir=\"$work_dir/extract\"",
        "stage_dir=\"$work_dir/stage\"",
        "if [ \"$use_local_release_assets\" = true ]; then",
        "  [ \"$DAY_SHIFT_VERSION\" = latest ] || [ \"$DAY_SHIFT_VERSION\" = \"$resolved_version\" ] || fail \"Local installer assets are for version $resolved_version, but DAY_SHIFT_VERSION requested $DAY_SHIFT_VERSION.\"",
        "  archive_path=\"$script_dir/$archive_filename\"",
        "  [ -f \"$archive_path\" ] || fail \"Local installer expected archive '$archive_filename' next to install.sh.\"",
        "else",
        "  archive_url=\"${DAY_SHIFT_ORIGIN%/}/$resolved_version/$archive_filename\"",
        "  curl -fsSL \"$archive_url\" -o \"$archive_path\"",
        "fi",
        "printf '%s  %s\\n' \"$archive_sha256\" \"$archive_path\" | shasum -a 256 -c - >/dev/null",
        "mkdir -p \"$extract_dir\" \"$stage_dir\"",
        "tar -xzf \"$archive_path\" -C \"$extract_dir\"",
        "payload_root=$(find \"$extract_dir\" -mindepth 1 -maxdepth 1 -type d | head -n 1)",
        "[ -n \"$payload_root\" ] || fail 'Downloaded archive did not contain an installable payload directory.'",
        "cp -R \"$payload_root/.\" \"$stage_dir/\"",
        "mkdir -p \"$DAY_SHIFT_INSTALL_DIR\" \"$DAY_SHIFT_BIN_DIR\"",
        "rm -rf \"$DAY_SHIFT_INSTALL_DIR.previous\"",
        "if [ -e \"$DAY_SHIFT_INSTALL_DIR/current\" ]; then",
        "  mv \"$DAY_SHIFT_INSTALL_DIR/current\" \"$DAY_SHIFT_INSTALL_DIR.previous\"",
        "fi",
        "mv \"$stage_dir\" \"$DAY_SHIFT_INSTALL_DIR/current\"",
        "ln -sf \"$DAY_SHIFT_INSTALL_DIR/current/bin/$DAY_SHIFT_COMMAND_NAME\" \"$DAY_SHIFT_BIN_DIR/$DAY_SHIFT_COMMAND_NAME\"",
        "printf 'Installed Day Shift %s\\n' \"$resolved_version\"",
        "printf 'Executable: %s\\n' \"$DAY_SHIFT_BIN_DIR/$DAY_SHIFT_COMMAND_NAME\"",
        "printf 'Command: %s\\n' \"$DAY_SHIFT_COMMAND_NAME\"",
        "printf 'If %s is not found, run: export PATH=\"%s:$PATH\"\\n' \"$DAY_SHIFT_COMMAND_NAME\" \"$DAY_SHIFT_BIN_DIR\"",
        "printf 'Persist PATH in your shell profile (for example ~/.zshrc or ~/.bashrc), then open a new shell or source the profile.\\n'",
        ""
    ].join("\n");
}
function shellQuote(value) {
    return `'${value.replace(/'/g, "'\\''")}'`;
}
function renderWindowsInstallerAsset(options) {
    return [
        "$ErrorActionPreference = 'Stop'",
        "",
        `[string]$DayShiftDefaultVersion = ${powershellQuote(options.defaultVersion)}`,
        `[string]$DayShiftCommandName = ${powershellQuote(options.installedCommandName)}`,
        `[string]$DayShiftOrigin = ${powershellQuote(options.officialOrigin)}`,
        "$DayShiftVersion = if ($env:DAY_SHIFT_VERSION) { $env:DAY_SHIFT_VERSION } else { $DayShiftDefaultVersion }",
        "$DayShiftInstallDir = if ($env:DAY_SHIFT_INSTALL_DIR) { $env:DAY_SHIFT_INSTALL_DIR } else { Join-Path $HOME 'AppData\\Local\\DayShift' }",
        "$DayShiftBinDir = if ($env:DAY_SHIFT_BIN_DIR) { $env:DAY_SHIFT_BIN_DIR } else { Join-Path $HOME 'AppData\\Local\\Microsoft\\WindowsApps' }",
        "",
        "function Fail([string]$Message) {",
        "  Write-Error $Message",
        "  exit 1",
        "}",
        "",
        "function Test-Node20 {",
        "  $nodeVersion = (& node --version 2>$null)",
        "  if (-not $nodeVersion) { return $false }",
        "  $nodeVersion = $nodeVersion.TrimStart('v')",
        "  $major = ($nodeVersion -split '\\.')[0]",
        "  $majorNumber = 0",
        "  if (-not [int]::TryParse($major, [ref]$majorNumber)) { return $false }",
        "  return $majorNumber -ge 20",
        "}",
        "",
        "if (-not (Get-Command node -ErrorAction SilentlyContinue)) { Fail 'Missing required command: node' }",
        "if (-not (Test-Node20)) { Fail 'Day Shift requires system Node.js >=20.0.0 before installation changes are made.' }",
        "",
        "$origin = $DayShiftOrigin.TrimEnd('/')",
        "$manifestUrl = \"$origin/release-manifest.json\"",
        "if ($DayShiftVersion -ne 'latest') {",
        "  $manifestUrl = \"$origin/$DayShiftVersion/release-manifest.json\"",
        "}",
        "",
        "$workDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString('N'))",
        "New-Item -ItemType Directory -Path $workDir | Out-Null",
        "try {",
        "  $manifestPath = Join-Path $workDir 'release-manifest.json'",
        "  Invoke-WebRequest -Uri $manifestUrl -OutFile $manifestPath",
        "  $manifest = Get-Content -Raw -Path $manifestPath | ConvertFrom-Json",
        "  if (-not $manifest.version -or $manifest.version -eq 'latest') { Fail 'Manifest did not resolve to an exact version.' }",
        "  $artifact = @($manifest.artifacts) | Where-Object { $_.target -eq 'windows' -and $_.kind -eq 'archive' -and $_.runtimeModel -eq 'system-node' } | Select-Object -First 1",
        "  if (-not $artifact) { Fail 'Manifest does not contain a Windows system-node archive.' }",
        "  if (-not ($artifact.sha256 -match '^[a-f0-9]{64}$')) { Fail 'Manifest archive checksum is missing or malformed.' }",
        "  if ($manifest.command.installedName) { $DayShiftCommandName = $manifest.command.installedName }",
        "  $archiveUrl = \"$origin/$($manifest.version)/$($artifact.filename)\"",
        "  $archivePath = Join-Path $workDir $artifact.filename",
        "  $extractDir = Join-Path $workDir 'extract'",
        "  $stageDir = Join-Path $workDir 'stage'",
        "  Invoke-WebRequest -Uri $archiveUrl -OutFile $archivePath",
        "  $actualHash = (Get-FileHash -Algorithm SHA256 -Path $archivePath).Hash.ToLowerInvariant()",
        "  if ($actualHash -ne $artifact.sha256) { Fail 'Downloaded archive SHA-256 did not match manifest metadata.' }",
        "  New-Item -ItemType Directory -Path $extractDir, $stageDir | Out-Null",
        "  Expand-Archive -Path $archivePath -DestinationPath $extractDir",
        "  $payloadRoot = Get-ChildItem -Path $extractDir -Directory | Select-Object -First 1",
        "  if (-not $payloadRoot) { Fail 'Downloaded archive did not contain an installable payload directory.' }",
        "  Copy-Item -Path (Join-Path $payloadRoot.FullName '*') -Destination $stageDir -Recurse",
        "  New-Item -ItemType Directory -Path $DayShiftInstallDir, $DayShiftBinDir -Force | Out-Null",
        "  $currentDir = Join-Path $DayShiftInstallDir 'current'",
        "  $previousDir = Join-Path $DayShiftInstallDir 'previous'",
        "  if (Test-Path $previousDir) { Remove-Item -Recurse -Force $previousDir }",
        "  if (Test-Path $currentDir) { Move-Item -Path $currentDir -Destination $previousDir }",
        "  Move-Item -Path $stageDir -Destination $currentDir",
        "  $sourceShim = Join-Path $currentDir \"bin\\$DayShiftCommandName.cmd\"",
        "  $targetShim = Join-Path $DayShiftBinDir \"$DayShiftCommandName.cmd\"",
        "  Copy-Item -Path $sourceShim -Destination $targetShim -Force",
        "  Write-Output \"Installed Day Shift $($manifest.version)\"",
        "  Write-Output \"Executable: $targetShim\"",
        "  Write-Output \"Command: $DayShiftCommandName\"",
        "  Write-Output \"Add $DayShiftBinDir to PATH if $DayShiftCommandName is not already available.\"",
        "} finally {",
        "  if (Test-Path $workDir) { Remove-Item -Recurse -Force $workDir }",
        "}",
        ""
    ].join("\r\n");
}
function renderPosixUninstallerAsset(options) {
    return [
        "#!/usr/bin/env sh",
        "set -eu",
        "",
        `DAY_SHIFT_COMMAND_NAME=${shellQuote(options.installedCommandName)}`,
        "DAY_SHIFT_INSTALL_DIR=${DAY_SHIFT_INSTALL_DIR:-$HOME/.local/day-shift}",
        "DAY_SHIFT_BIN_DIR=${DAY_SHIFT_BIN_DIR:-$HOME/.local/bin}",
        "",
        "rm -f \"$DAY_SHIFT_BIN_DIR/$DAY_SHIFT_COMMAND_NAME\"",
        "rm -rf \"$DAY_SHIFT_INSTALL_DIR\"",
        "printf 'Removed Day Shift installation directory: %s\\n' \"$DAY_SHIFT_INSTALL_DIR\"",
        "printf 'Removed command shim or symlink: %s\\n' \"$DAY_SHIFT_BIN_DIR/$DAY_SHIFT_COMMAND_NAME\"",
        ""
    ].join("\n");
}
function renderWindowsUninstallerAsset(options) {
    return [
        "$ErrorActionPreference = 'Stop'",
        "",
        `[string]$DayShiftCommandName = ${powershellQuote(options.installedCommandName)}`,
        "$DayShiftInstallDir = if ($env:DAY_SHIFT_INSTALL_DIR) { $env:DAY_SHIFT_INSTALL_DIR } else { Join-Path $HOME 'AppData\\Local\\DayShift' }",
        "$DayShiftBinDir = if ($env:DAY_SHIFT_BIN_DIR) { $env:DAY_SHIFT_BIN_DIR } else { Join-Path $HOME 'AppData\\Local\\Microsoft\\WindowsApps' }",
        "",
        "$cmdShimPath = Join-Path $DayShiftBinDir \"$DayShiftCommandName.cmd\"",
        "$ps1ShimPath = Join-Path $DayShiftBinDir \"$DayShiftCommandName.ps1\"",
        "if (Test-Path $cmdShimPath) { Remove-Item -Force $cmdShimPath }",
        "if (Test-Path $ps1ShimPath) { Remove-Item -Force $ps1ShimPath }",
        "if (Test-Path $DayShiftInstallDir) { Remove-Item -Recurse -Force $DayShiftInstallDir }",
        "Write-Output \"Removed Day Shift installation directory: $DayShiftInstallDir\"",
        "Write-Output \"Removed command shims from: $DayShiftBinDir\"",
        ""
    ].join("\r\n");
}
function powershellQuote(value) {
    return `'${value.replace(/'/g, "''")}'`;
}
function createReleaseInstallationReadmeMetadataFromManifest(manifest) {
    const normalizedManifest = normalizeReleaseManifestData(manifest);
    return {
        version: normalizedManifest.version,
        command: normalizedManifest.command.installedName,
        requiredNodeVersion: normalizedManifest.runtime.requiredNodeVersion,
        installation: normalizedManifest.installation,
        archives: [
            requireReadmeArchiveArtifact(normalizedManifest, "macos-linux"),
            requireReadmeArchiveArtifact(normalizedManifest, "windows")
        ].map((artifact) => ({
            target: artifact.target,
            filename: artifact.filename,
            path: artifact.path,
            supportedOs: artifact.supportedOs
        }))
    };
}
function createReleaseInstallationReadmeMetadataFromBuildMetadata(options) {
    if (options.state.versionDirectoryRelative === null) {
        throw (0, errors_1.createUsageError)("Release installation README requires a resolved version directory.", [
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    const packageSlug = normalizeArchiveNamePart(options.packageName ?? "day-shift");
    const posixArchiveFilename = `${packageSlug}-${options.version}-macos-linux-system-node.tar.gz`;
    const windowsArchiveFilename = `${packageSlug}-${options.version}-windows-system-node.zip`;
    return {
        version: options.version,
        command: options.installedCommandName,
        requiredNodeVersion: requireManifestString(options.requiredNodeVersion, "Node.js requirement", "missing-node-version-requirement"),
        installation: {
            readmePath: `${resolveRequiredDownloadBundleRelativeDirectory(options.state)}/${exports.RELEASE_INSTALLATION_README_FILENAME}`,
            defaultInstallDirectory: {
                posix: exports.DEFAULT_POSIX_INSTALL_DIRECTORY,
                windows: exports.DEFAULT_WINDOWS_INSTALL_DIRECTORY
            },
            defaultBinDirectory: {
                posix: exports.DEFAULT_POSIX_BIN_DIRECTORY,
                windows: exports.DEFAULT_WINDOWS_BIN_DIRECTORY
            }
        },
        archives: [
            {
                target: "macos-linux",
                filename: posixArchiveFilename,
                path: `${resolveRequiredDownloadBundleRelativeDirectory(options.state)}/${posixArchiveFilename}`,
                supportedOs: ["darwin", "linux"]
            },
            {
                target: "windows",
                filename: windowsArchiveFilename,
                path: `${resolveRequiredDownloadBundleRelativeDirectory(options.state)}/${windowsArchiveFilename}`,
                supportedOs: ["win32"]
            }
        ]
    };
}
function renderReleaseInstallationReadme(options) {
    const command = options.metadata.command;
    const posixArchive = requireReadmeArchiveMetadata(options.metadata, "macos-linux");
    const windowsArchive = requireReadmeArchiveMetadata(options.metadata, "windows");
    const posixInstallDir = options.metadata.installation.defaultInstallDirectory.posix;
    const posixBinDir = options.metadata.installation.defaultBinDirectory.posix;
    const windowsInstallDir = options.metadata.installation.defaultInstallDirectory.windows;
    const windowsBinDir = options.metadata.installation.defaultBinDirectory.windows;
    const posixInstallDirShell = shellInstallPathExpression(posixInstallDir);
    const posixBinDirShell = shellInstallPathExpression(posixBinDir);
    const windowsInstallDirPowerShell = powershellInstallPathExpression(windowsInstallDir);
    const windowsBinDirPowerShell = powershellInstallPathExpression(windowsBinDir);
    return [
        `# Day Shift ${options.metadata.version} Installation`,
        "",
        `This guide assumes you are reading it from an extracted Day Shift ${options.metadata.version} archive root. It installs the \`${command}\` command using system Node.js ${options.metadata.requiredNodeVersion} and does not require public npm publication.`,
        "",
        "## Extracted Archive",
        "",
        `- POSIX archive for darwin and linux on x64 or arm64: \`${posixArchive.filename}\` (${posixArchive.path})`,
        `- Windows archive for win32 on x64 or arm64: \`${windowsArchive.filename}\` (${windowsArchive.path})`,
        "- Launchers: `bin/`",
        "- Release notes: `release-notes.md`",
        "",
        "## Prerequisites",
        "",
        `- Install system Node.js ${options.metadata.requiredNodeVersion} before changing an existing installation.`,
        "- Run commands from the extracted archive root.",
        "- Use a user-owned install directory.",
        "",
        "First-run validation from the extracted archive root:",
        "",
        "```sh",
        `./bin/${command} --version`,
        `./bin/${command} about`,
        "```",
        "",
        "## POSIX Shell",
        "",
        `Default install directory: \`${posixInstallDir}\``,
        `Default PATH directory: \`${posixBinDir}\``,
        "",
        "```sh",
        `INSTALL_DIR=${posixInstallDirShell}`,
        `BIN_DIR=${posixBinDirShell}`,
        "STAGE_DIR=\"$INSTALL_DIR/.install-stage\"",
        "mkdir -p \"$INSTALL_DIR\" \"$BIN_DIR\"",
        "rm -rf \"$STAGE_DIR\"",
        "mkdir -p \"$STAGE_DIR\"",
        "cp -R . \"$STAGE_DIR\"",
        "rm -rf \"$INSTALL_DIR/current\"",
        "mv \"$STAGE_DIR\" \"$INSTALL_DIR/current\"",
        `ln -sf "$INSTALL_DIR/current/bin/${command}" "$BIN_DIR/${command}"`,
        `"$BIN_DIR/${command}" --version`,
        `"$BIN_DIR/${command}" about`,
        "```",
        "",
        "Direct execution before PATH setup:",
        "",
        "```sh",
        `./bin/${command} --version`,
        `./bin/${command} about`,
        "```",
        "",
        `Add \`${posixBinDir}\` to PATH if \`${command}\` is not found after installation.`,
        "",
        "macOS usually uses `zsh` by default:",
        "",
        "```sh",
        `echo 'export PATH="${posixBinDir}:$PATH"' >> ~/.zshrc`,
        "source ~/.zshrc",
        `command -v ${command}`,
        "```",
        "",
        "If you use `bash` instead:",
        "",
        "```sh",
        `echo 'export PATH="${posixBinDir}:$PATH"' >> ~/.bashrc`,
        "source ~/.bashrc",
        `command -v ${command}`,
        "```",
        "",
        "If the current shell still does not resolve the command, open a new terminal or run it directly from the install path:",
        "",
        "```sh",
        `"$HOME/.local/day-shift/current/bin/${command}" --version`,
        `"$HOME/.local/day-shift/current/bin/${command}" about`,
        "```",
        "",
        "## Windows PowerShell",
        "",
        `Default install directory: \`${windowsInstallDir}\``,
        `Default PATH directory: \`${windowsBinDir}\``,
        "",
        "```powershell",
        `$InstallDir = ${windowsInstallDirPowerShell}`,
        `$BinDir = ${windowsBinDirPowerShell}`,
        "$StageDir = Join-Path $InstallDir '.install-stage'",
        "$CurrentDir = Join-Path $InstallDir 'current'",
        "New-Item -ItemType Directory -Force -Path $InstallDir, $BinDir | Out-Null",
        "if (Test-Path $StageDir) { Remove-Item -Recurse -Force $StageDir }",
        "New-Item -ItemType Directory -Force -Path $StageDir | Out-Null",
        "Get-ChildItem -Force -Path . | Copy-Item -Destination $StageDir -Recurse -Force",
        "if (Test-Path $CurrentDir) { Remove-Item -Recurse -Force $CurrentDir }",
        "Move-Item -Path $StageDir -Destination $CurrentDir",
        `$Shim = Join-Path $CurrentDir 'bin\\${command}.cmd'`,
        `$Target = Join-Path $BinDir '${command}.cmd'`,
        "Copy-Item -Force -Path $Shim -Destination $Target",
        `& $Target --version`,
        `& $Target about`,
        "```",
        "",
        "Direct execution before PATH setup:",
        "",
        "```powershell",
        `$Direct = Join-Path (Join-Path (Get-Location) 'bin') '${command}.cmd'`,
        "& $Direct --version",
        "& $Direct about",
        "```",
        "",
        `Add \`${windowsBinDir}\` to PATH if \`${command}\` is not found after installation. On Windows, open a new terminal after changing PATH.`,
        "",
        "## Beta and VIP signed license file",
        "",
        ...(0, license_guidance_1.renderBetaVipSignedLicenseGuidanceLines)(command).map((line) => `- ${line}`),
        "",
        "## Upgrade or Reinstall",
        "",
        "Repeat the platform install steps from a newly extracted archive. The manual install flow stages files before switching `current`; if a command fails before the switch, the previous install remains in place.",
        "",
        "## Uninstall",
        "",
        "Remove the install directory and the command shim or symlink from the PATH directory.",
        "",
        "```sh",
        `rm -rf ${posixInstallDirShell} ${shellInstallPathExpression(`${posixBinDir}/${command}`)}`,
        "```",
        "",
        "```powershell",
        `Remove-Item -Recurse -Force ${windowsInstallDirPowerShell}`,
        `Remove-Item -Force ${powershellInstallPathExpression(`${windowsBinDir}\\${command}.cmd`)}`,
        "```",
        "",
        "## Troubleshooting",
        "",
        `- Node.js prerequisite: run \`node --version\` and install Node.js ${options.metadata.requiredNodeVersion} if \`${command}\` does not start.`,
        `- PATH setup: if \`${command}\` is not found, run it directly from \`./bin\` or from the install path, add the PATH directory shown above, and reload your shell profile or open a new terminal.`,
        "- Archive choice: use the darwin/linux archive for POSIX shell systems and the win32 archive for Windows PowerShell.",
        "- Replacement failure: retry after fixing permissions or locks; the staged install directory can be removed safely before retrying.",
        "",
        "## Support handoff",
        "",
        `When requesting support, include the release version \`${options.metadata.version}\`, the platform archive filename, the output from \`./bin/${command} --version\`, the output from \`./bin/${command} about\`, and any install error text.`,
        ""
    ].join("\n");
}
function requireReadmeArchiveArtifact(manifest, target) {
    const artifact = manifest.artifacts.find((candidate) => candidate.kind === "archive" &&
        candidate.target === target &&
        candidate.runtimeModel === "system-node");
    if (artifact === undefined) {
        throw (0, errors_1.createUsageError)("Release installation README requires archive metadata for every supported platform.", [
            "Reason: installation-readme-missing-archive",
            `Target: ${target}`,
            "Recovery: regenerate release archives, checksums, and manifest metadata before README generation.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    return artifact;
}
function requireReadmeArchiveMetadata(metadata, target) {
    const artifact = metadata.archives.find((candidate) => candidate.target === target);
    if (artifact === undefined) {
        throw (0, errors_1.createUsageError)("Release installation README requires archive metadata for every supported platform.", [
            "Reason: installation-readme-missing-archive",
            `Target: ${target}`,
            "Recovery: regenerate release archives and README metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    return artifact;
}
function copyArchiveRootDocumentation(options) {
    const bundleDirectory = node_path_1.default.join(options.versionDirectory, exports.RELEASE_DOWNLOAD_BUNDLE_DIRECTORY);
    for (const filename of [exports.RELEASE_INSTALLATION_README_FILENAME, "release-notes.md"]) {
        const sourcePath = node_path_1.default.join(bundleDirectory, filename);
        if (!node_fs_1.default.existsSync(sourcePath) || !node_fs_1.default.statSync(sourcePath).isFile()) {
            throw (0, errors_1.createUsageError)("Release archive payload requires generated download bundle documentation.", [
                "Reason: archive-root-documentation-missing",
                `Missing file: ${filename}`,
                "Recovery: regenerate release notes and installation guidance before archive creation.",
                "Exit behavior: exits before successful-release state is recorded."
            ]);
        }
        node_fs_1.default.copyFileSync(sourcePath, node_path_1.default.join(options.targetDirectory, filename));
    }
}
function shellWord(value) {
    return shellQuote(value);
}
function shellInstallPathExpression(value) {
    if (value === "$HOME") {
        return '"$HOME"';
    }
    if (value.startsWith("$HOME/")) {
        return `"$HOME/${value.slice("$HOME/".length)}"`;
    }
    return shellWord(value);
}
function powershellInstallPathExpression(value) {
    if (value === "$HOME") {
        return "$HOME";
    }
    if (value.startsWith("$HOME\\")) {
        return `(Join-Path $HOME ${powershellQuote(value.slice("$HOME\\".length))})`;
    }
    return powershellQuote(value);
}
function writeDeterministicTarGzipArchive(options) {
    const blocks = [];
    for (const filePath of options.filePaths) {
        const absoluteFilePath = node_path_1.default.join(options.sourceDirectory, ...filePath.split("/"));
        const fileContents = node_fs_1.default.readFileSync(absoluteFilePath);
        const stats = node_fs_1.default.statSync(absoluteFilePath);
        const entryName = `${options.rootDirectoryName}/${filePath}`;
        blocks.push(createTarHeader({
            name: entryName,
            size: fileContents.length,
            mode: stats.mode & 0o777
        }));
        blocks.push(fileContents);
        const paddingLength = (512 - (fileContents.length % 512)) % 512;
        if (paddingLength > 0) {
            blocks.push(Buffer.alloc(paddingLength));
        }
    }
    blocks.push(Buffer.alloc(1024));
    node_fs_1.default.writeFileSync(options.archivePath, node_zlib_1.default.gzipSync(Buffer.concat(blocks), { level: 9 }));
}
function createTarHeader(options) {
    const header = Buffer.alloc(512);
    const nameParts = splitTarPath(options.name);
    writeTarString(header, 0, 100, nameParts.name);
    writeTarOctal(header, 100, 8, options.mode);
    writeTarOctal(header, 108, 8, 0);
    writeTarOctal(header, 116, 8, 0);
    writeTarOctal(header, 124, 12, options.size);
    writeTarOctal(header, 136, 12, 0);
    header.fill(0x20, 148, 156);
    header[156] = "0".charCodeAt(0);
    writeTarString(header, 257, 6, "ustar");
    writeTarString(header, 263, 2, "00");
    writeTarString(header, 265, 32, "root");
    writeTarString(header, 297, 32, "root");
    if (nameParts.prefix.length > 0) {
        writeTarString(header, 345, 155, nameParts.prefix);
    }
    let checksum = 0;
    for (const byte of header) {
        checksum += byte;
    }
    writeTarOctal(header, 148, 8, checksum);
    return header;
}
function splitTarPath(value) {
    if (Buffer.byteLength(value) <= 100) {
        return { name: value, prefix: "" };
    }
    const slashIndex = value.lastIndexOf("/");
    const prefix = slashIndex === -1 ? "" : value.slice(0, slashIndex);
    const name = slashIndex === -1 ? value : value.slice(slashIndex + 1);
    if (Buffer.byteLength(name) > 100 || Buffer.byteLength(prefix) > 155) {
        throw (0, errors_1.createUsageError)("Release tar archive entry path is too long.", [
            "Reason: archive-entry-path-too-long",
            `Entry: ${value}`,
            "Recovery: shorten generated release payload paths before archive materialization.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    return { name, prefix };
}
function writeTarString(buffer, offset, length, value) {
    buffer.write(value, offset, length, "utf8");
}
function writeTarOctal(buffer, offset, length, value) {
    const text = value.toString(8).padStart(length - 1, "0").slice(-(length - 1));
    buffer.write(text, offset, length - 1, "ascii");
    buffer[offset + length - 1] = 0;
}
function writeDeterministicZipArchive(options) {
    const localRecords = [];
    const centralRecords = [];
    let offset = 0;
    for (const filePath of options.filePaths) {
        const absoluteFilePath = node_path_1.default.join(options.sourceDirectory, ...filePath.split("/"));
        const fileContents = node_fs_1.default.readFileSync(absoluteFilePath);
        const stats = node_fs_1.default.statSync(absoluteFilePath);
        const entryName = `${options.rootDirectoryName}/${filePath}`;
        const nameBuffer = Buffer.from(entryName, "utf8");
        const crc = computeCrc32(fileContents);
        const mode = stats.mode & 0o777;
        const localHeader = Buffer.alloc(30);
        localHeader.writeUInt32LE(0x04034b50, 0);
        localHeader.writeUInt16LE(20, 4);
        localHeader.writeUInt16LE(0x0800, 6);
        localHeader.writeUInt16LE(0, 8);
        localHeader.writeUInt16LE(0, 10);
        localHeader.writeUInt16LE(0x21, 12);
        localHeader.writeUInt32LE(crc, 14);
        localHeader.writeUInt32LE(fileContents.length, 18);
        localHeader.writeUInt32LE(fileContents.length, 22);
        localHeader.writeUInt16LE(nameBuffer.length, 26);
        localHeader.writeUInt16LE(0, 28);
        localRecords.push(localHeader, nameBuffer, fileContents);
        const centralHeader = Buffer.alloc(46);
        centralHeader.writeUInt32LE(0x02014b50, 0);
        centralHeader.writeUInt16LE(0x0314, 4);
        centralHeader.writeUInt16LE(20, 6);
        centralHeader.writeUInt16LE(0x0800, 8);
        centralHeader.writeUInt16LE(0, 10);
        centralHeader.writeUInt16LE(0, 12);
        centralHeader.writeUInt16LE(0x21, 14);
        centralHeader.writeUInt32LE(crc, 16);
        centralHeader.writeUInt32LE(fileContents.length, 20);
        centralHeader.writeUInt32LE(fileContents.length, 24);
        centralHeader.writeUInt16LE(nameBuffer.length, 28);
        centralHeader.writeUInt16LE(0, 30);
        centralHeader.writeUInt16LE(0, 32);
        centralHeader.writeUInt16LE(0, 34);
        centralHeader.writeUInt16LE(0, 36);
        centralHeader.writeUInt32LE((mode << 16) >>> 0, 38);
        centralHeader.writeUInt32LE(offset, 42);
        centralRecords.push(centralHeader, nameBuffer);
        offset += localHeader.length + nameBuffer.length + fileContents.length;
    }
    const centralDirectory = Buffer.concat(centralRecords);
    const endRecord = Buffer.alloc(22);
    endRecord.writeUInt32LE(0x06054b50, 0);
    endRecord.writeUInt16LE(0, 4);
    endRecord.writeUInt16LE(0, 6);
    endRecord.writeUInt16LE(options.filePaths.length, 8);
    endRecord.writeUInt16LE(options.filePaths.length, 10);
    endRecord.writeUInt32LE(centralDirectory.length, 12);
    endRecord.writeUInt32LE(offset, 16);
    endRecord.writeUInt16LE(0, 20);
    node_fs_1.default.writeFileSync(options.archivePath, Buffer.concat([...localRecords, centralDirectory, endRecord]));
}
function computeCrc32(buffer) {
    let crc = 0xffffffff;
    for (const byte of buffer) {
        crc = CRC32_TABLE[(crc ^ byte) & 0xff] ^ (crc >>> 8);
    }
    return (crc ^ 0xffffffff) >>> 0;
}
const CRC32_TABLE = Array.from({ length: 256 }, (_unused, index) => {
    let value = index;
    for (let bit = 0; bit < 8; bit += 1) {
        value = (value & 1) === 1
            ? 0xedb88320 ^ (value >>> 1)
            : value >>> 1;
    }
    return value >>> 0;
});
function copyRepositoryNoticeFiles(options) {
    const noticeFiles = node_fs_1.default.readdirSync(options.repositoryRoot, { withFileTypes: true })
        .filter((entry) => entry.isFile())
        .map((entry) => entry.name)
        .filter((name) => /^(?:LICENSE|NOTICE|THIRD_PARTY_NOTICES)(?:\..*)?$/i.test(name))
        .sort();
    for (const noticeFile of noticeFiles) {
        node_fs_1.default.copyFileSync(node_path_1.default.join(options.repositoryRoot, noticeFile), node_path_1.default.join(options.stagingDirectory, noticeFile));
    }
    return noticeFiles;
}
function copyDirectoryFiltered(options) {
    if (!node_fs_1.default.existsSync(options.sourceDirectory) || !node_fs_1.default.statSync(options.sourceDirectory).isDirectory()) {
        return;
    }
    for (const entry of node_fs_1.default.readdirSync(options.sourceDirectory, { withFileTypes: true })) {
        const sourcePath = node_path_1.default.join(options.sourceDirectory, entry.name);
        const destinationPath = node_path_1.default.join(options.destinationDirectory, entry.name);
        const payloadRelativePath = toRepositoryRelativePath(options.payloadRoot, destinationPath);
        const forbidden = classifyForbiddenReleasePayloadFile(payloadRelativePath);
        if (forbidden !== null) {
            continue;
        }
        if (entry.isDirectory()) {
            copyDirectoryFiltered({
                sourceDirectory: sourcePath,
                destinationDirectory: destinationPath,
                payloadRoot: options.payloadRoot
            });
        }
        else if (entry.isFile()) {
            node_fs_1.default.mkdirSync(node_path_1.default.dirname(destinationPath), { recursive: true });
            node_fs_1.default.copyFileSync(sourcePath, destinationPath);
        }
        else if (entry.isSymbolicLink()) {
            node_fs_1.default.mkdirSync(node_path_1.default.dirname(destinationPath), { recursive: true });
            node_fs_1.default.symlinkSync(node_fs_1.default.readlinkSync(sourcePath), destinationPath);
        }
    }
}
function copyRepositorySharedAuthoringDirectories(options) {
    for (const filePlan of [
        {
            sourcePath: resolveReleaseAgentEntrypointSourcePath(options.repositoryRoot),
            destinationRelativePath: ".day-shift/agents.md"
        },
        {
            sourcePath: node_path_1.default.join(options.repositoryRoot, ".day-shift", ".markdownlint.json"),
            destinationRelativePath: ".day-shift/.markdownlint.json"
        }
    ]) {
        const sourcePath = filePlan.sourcePath;
        const relativeFile = filePlan.destinationRelativePath;
        const destinationPath = node_path_1.default.join(options.stagingDirectory, ...relativeFile.split("/"));
        if (!node_fs_1.default.existsSync(sourcePath) || !node_fs_1.default.statSync(sourcePath).isFile()) {
            continue;
        }
        const forbidden = classifyForbiddenReleasePayloadFile(toRepositoryRelativePath(options.stagingDirectory, destinationPath));
        if (forbidden !== null) {
            continue;
        }
        node_fs_1.default.mkdirSync(node_path_1.default.dirname(destinationPath), { recursive: true });
        node_fs_1.default.copyFileSync(sourcePath, destinationPath);
    }
    for (const relativeDirectory of default_assets_1.SHARED_AUTHORING_SOURCE_ROOTS) {
        copyDirectoryFiltered({
            sourceDirectory: node_path_1.default.join(options.repositoryRoot, ...relativeDirectory.split("/")),
            destinationDirectory: node_path_1.default.join(options.stagingDirectory, ...relativeDirectory.split("/")),
            payloadRoot: options.stagingDirectory
        });
    }
}
function resolveReleaseAgentEntrypointSourcePath(repositoryRoot) {
    const scaffoldEntrypointPath = node_path_1.default.join(repositoryRoot, ".day-shift", "scaffolds", "agents.init.md");
    if (node_fs_1.default.existsSync(scaffoldEntrypointPath) && node_fs_1.default.statSync(scaffoldEntrypointPath).isFile()) {
        return scaffoldEntrypointPath;
    }
    return node_path_1.default.join(repositoryRoot, ".day-shift", "agents.md");
}
function removePublicCliExcludedPrivateRuntimeFiles(stagingDirectory) {
    for (const relativePath of PUBLIC_CLI_EXCLUDED_PRIVATE_RUNTIME_FILES) {
        removeIfExists(node_path_1.default.join(stagingDirectory, ...relativePath.split("/")));
    }
}
function validateReleasePayloadContent(options) {
    const fileSet = new Set(options.files);
    const missingRequiredFiles = options.requiredFiles.filter((requiredFile) => !fileSet.has(requiredFile));
    const forbiddenFiles = options.files
        .map((filePath) => classifyForbiddenReleasePayloadFile(filePath))
        .filter((finding) => finding !== null);
    if (missingRequiredFiles.length > 0 || forbiddenFiles.length > 0) {
        throw (0, errors_1.createUsageError)(`Release ${options.payload} payload content validation failed.`, [
            "Reason: forbidden-or-missing-release-payload-content",
            `Missing required files: ${missingRequiredFiles.length === 0 ? "(none)" : missingRequiredFiles.join(", ")}`,
            `Forbidden files: ${forbiddenFiles.length === 0 ? "(none)" : forbiddenFiles.map((finding) => `${finding.path} [${finding.group}] ${finding.reason}`).join("; ")}`,
            "Recovery: fix release staging/archive payload rules so required runtime files remain and forbidden local or development files are excluded.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    return {
        status: "passed",
        payload: options.payload,
        requiredFiles: [...options.requiredFiles],
        forbiddenFiles: []
    };
}
function classifyForbiddenReleasePayloadFile(filePath) {
    const normalizedPath = normalizePayloadPath(filePath);
    const segments = normalizedPath.split("/");
    const basename = segments[segments.length - 1] ?? normalizedPath;
    if (normalizedPath.startsWith(".day-shift/specs/")) {
        return createForbiddenPayloadFile(normalizedPath, "planning-specs", "planning specification artifacts are not runtime payload files");
    }
    if (normalizedPath.startsWith(".day-shift/planning/")) {
        return createForbiddenPayloadFile(normalizedPath, "planning-artifacts", "planning artifacts are not runtime payload files");
    }
    if (normalizedPath.startsWith(".day-shift/state/") ||
        normalizedPath.startsWith(".day-shift/local/") ||
        normalizedPath.startsWith(".day-shift/checkpoints/")) {
        return createForbiddenPayloadFile(normalizedPath, "local-day-shift-state", "local Day Shift state must not be released");
    }
    if (normalizedPath.startsWith("sandbox/") || normalizedPath.includes("/sandbox/")) {
        return createForbiddenPayloadFile(normalizedPath, "sandbox-output", "sandbox output is development-only");
    }
    if (segments.includes("test") || segments.includes("tests") || /\.test\.[cm]?[jt]s$/.test(basename)) {
        return createForbiddenPayloadFile(normalizedPath, "tests", "tests are development-only");
    }
    if (segments.includes("fixture") || segments.includes("fixtures")) {
        return createForbiddenPayloadFile(normalizedPath, "fixtures", "fixtures are development-only");
    }
    if (isPackageManagerCachePath(normalizedPath, segments)) {
        return createForbiddenPayloadFile(normalizedPath, "package-manager-cache", "package-manager caches are local generated state");
    }
    if (isLocalLicenseStatePath(normalizedPath, basename)) {
        return createForbiddenPayloadFile(normalizedPath, "local-license-state", "local license state is user-specific and must not be released");
    }
    if (isReminderStatePath(normalizedPath, basename)) {
        return createForbiddenPayloadFile(normalizedPath, "reminder-state", "reminder state and generated reminder artifacts are user-specific");
    }
    if (isConfigOverridePath(normalizedPath, basename)) {
        return createForbiddenPayloadFile(normalizedPath, "config-overrides", "local configuration overrides are user-specific");
    }
    const privateLicenseOperation = classifyPrivateLicenseOperationPath(normalizedPath, segments, basename);
    if (privateLicenseOperation !== null) {
        return privateLicenseOperation;
    }
    if (/\.(?:ts|tsx)$/.test(basename) && !isRuntimeRequiredTypeScriptPath(normalizedPath)) {
        return createForbiddenPayloadFile(normalizedPath, "source-typescript", "source TypeScript files are excluded unless explicitly runtime-required");
    }
    return null;
}
function isPackageManagerCachePath(normalizedPath, segments) {
    return normalizedPath === ".pnpm-store" ||
        normalizedPath.startsWith(".pnpm-store/") ||
        normalizedPath === ".npm" ||
        normalizedPath.startsWith(".npm/") ||
        normalizedPath === ".yarn/cache" ||
        normalizedPath.startsWith(".yarn/cache/") ||
        segments.includes(".cache") ||
        segments.includes(".pnpm-store") ||
        segments.includes(".npm") ||
        segments.includes(".turbo");
}
function isLocalLicenseStatePath(normalizedPath, basename) {
    return normalizedPath.startsWith(".day-shift/licenses/") ||
        normalizedPath.startsWith(".day-shift/license/") ||
        /^\.?day-shift-license(?:[._-].*)?$/i.test(basename) ||
        /^license(?:[._-]state|[._-]local)\./i.test(basename);
}
function isReminderStatePath(normalizedPath, basename) {
    return normalizedPath.startsWith(".day-shift/reminders/") ||
        normalizedPath.startsWith(".day-shift/managed-reminders/") ||
        /^\.?day-shift-reminder/i.test(basename) ||
        /^managed-reminder/i.test(basename) ||
        /^reminder(?:[._-]state|[._-]local)\.(?:json|ya?ml|toml|txt|log)$/i.test(basename);
}
function isConfigOverridePath(normalizedPath, basename) {
    return normalizedPath.startsWith(".day-shift/config.local") ||
        normalizedPath.startsWith(".day-shift/config/") ||
        /^day-shift\.config\.local\./i.test(basename) ||
        /^\.env(?:\.|$)/.test(basename);
}
function classifyPrivateLicenseOperationPath(normalizedPath, segments, basename) {
    if (segments.includes("local-license-ops")) {
        return createForbiddenPayloadFile(normalizedPath, "private-license-ops", "local license-issuer operator files must not be released");
    }
    if (/^issue-local-license\.(?:js|cjs|mjs)$/i.test(basename)) {
        return createForbiddenPayloadFile(normalizedPath, "private-license-issuer-script", "local issuer scripts must not be released");
    }
    if (/^license-issuer\.(?:js|cjs|mjs|map|d\.ts)$/i.test(basename)) {
        return createForbiddenPayloadFile(normalizedPath, "private-license-issuer-runtime", "compiled issuer helpers must not be released");
    }
    if (/^recipients\.(?:csv|json)$/i.test(basename)) {
        return createForbiddenPayloadFile(normalizedPath, "private-license-recipient-input", "recipient input is private license-operation data");
    }
    if (segments.includes("licenses-out")) {
        return createForbiddenPayloadFile(normalizedPath, "private-generated-license-output", "generated license batches must not be released");
    }
    if (/^(?:beta|vip)[\w.-]*licenses?[\w.-]*\.(?:json|zip|tgz|tar\.gz)$/i.test(basename)) {
        return createForbiddenPayloadFile(normalizedPath, "private-generated-license-batch", "generated beta/VIP license batches must not be released");
    }
    if (/^issuance-report\.json$/i.test(basename)) {
        return createForbiddenPayloadFile(normalizedPath, "private-license-issuance-report", "license issuance reports are private operator records");
    }
    if (isPrivateSigningKeyPath(basename)) {
        return createForbiddenPayloadFile(normalizedPath, "private-license-signing-key", "private signing keys must not be released");
    }
    if (isPrivateSupportRecordPath(normalizedPath, segments, basename)) {
        return createForbiddenPayloadFile(normalizedPath, "private-support-record", "private support records and archives must not be released");
    }
    return null;
}
function isPrivateSigningKeyPath(basename) {
    return /(?:private|signing|license|root)[\w.-]*(?:key|ed25519)[\w.-]*\.(?:pem|key|json)$/i.test(basename) ||
        /(?:key|ed25519)[\w.-]*(?:private|signing|license|root)[\w.-]*\.(?:pem|key|json)$/i.test(basename);
}
function isPrivateSupportRecordPath(normalizedPath, segments, basename) {
    return segments.includes("support-records") ||
        /\bsupport-records?\b/i.test(normalizedPath) ||
        /^support-archive\.(?:zip|tgz|tar\.gz|enc|json)$/i.test(basename) ||
        /^encrypted-support(?:-archive|-records?)?\.(?:zip|tgz|tar\.gz|enc|json)$/i.test(basename);
}
function isRuntimeRequiredTypeScriptPath(_normalizedPath) {
    return false;
}
function createForbiddenPayloadFile(pathValue, group, reason) {
    return {
        path: pathValue,
        group,
        reason
    };
}
function requireManifestString(value, label, reason) {
    if (typeof value === "string" && value.trim().length > 0) {
        return value;
    }
    throw (0, errors_1.createUsageError)(`Release manifest requires ${label}.`, [
        `Reason: ${reason}`,
        `Recovery: restore ${label} metadata before manifest generation.`,
        "Exit behavior: exits before successful-release state is recorded."
    ]);
}
function getSupportedOsForManifestTarget(target) {
    if (target === "windows" || target === "windows-installer") {
        return ["win32"];
    }
    return ["darwin", "linux"];
}
function createReleaseDownloadUrl(version, filename) {
    return new URL(`${version}/${filename}`, normalizeDownloadOrigin(exports.DEFAULT_RELEASE_DOWNLOAD_ORIGIN)).toString();
}
function createReleaseManifestSupportedPlatforms() {
    return [
        {
            os: "darwin",
            label: "macOS",
            architectures: ["x64", "arm64"],
            installer: "posix"
        },
        {
            os: "linux",
            label: "Linux",
            architectures: ["x64", "arm64"],
            installer: "posix"
        },
        {
            os: "win32",
            label: "Windows",
            architectures: ["x64", "arm64"],
            installer: "powershell"
        }
    ];
}
function validateManifestWebsiteRelativePath(value, label) {
    const normalizedPath = normalizePayloadPath(value);
    if (normalizedPath.length === 0 ||
        normalizedPath.startsWith("/") ||
        normalizedPath.includes("://") ||
        normalizedPath.split("/").includes("..")) {
        throw (0, errors_1.createUsageError)(`Release manifest ${label} must be a website-relative placeholder path.`, [
            `Reason: manifest-${label.replace(/\s+/g, "-")}-invalid`,
            `${label}: ${value}`,
            "Recovery: keep manifest public paths as website-relative placeholders until website publication runs.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
}
function validateManifestWebsiteDownloadPath(value, version, filename, label) {
    const expectedPath = `downloads/${version}/${filename}`;
    if (value !== expectedPath || value.includes("/latest/")) {
        throw (0, errors_1.createUsageError)(`Release manifest ${label} must use an immutable versioned downloads path.`, [
            `Reason: manifest-${label.replace(/\s+/g, "-")}-invalid`,
            `Expected path: ${expectedPath}`,
            `Received path: ${value}`,
            "Recovery: regenerate the manifest from exact release version metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
}
function validateManifestWebsiteDownloadUrl(value, version, filename, label) {
    const expectedUrl = createReleaseDownloadUrl(version, filename);
    if (value !== expectedUrl || value.includes("/latest/")) {
        throw (0, errors_1.createUsageError)(`Release manifest ${label} must use an immutable website-owned downloads URL.`, [
            `Reason: manifest-${label.replace(/\s+/g, "-")}-invalid`,
            `Expected URL: ${expectedUrl}`,
            `Received URL: ${value}`,
            "Recovery: regenerate the manifest from exact release version metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
}
function normalizeReleaseManifestSupportedPlatforms(value) {
    if (!Array.isArray(value)) {
        throw (0, errors_1.createUsageError)("Release manifest supported platform metadata is missing or malformed.", [
            "Reason: malformed-manifest-supported-platforms",
            "Recovery: regenerate the manifest from release platform metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    return value.map((platform) => normalizeReleaseManifestSupportedPlatform(platform));
}
function normalizeReleaseManifestSupportedPlatform(value) {
    if (typeof value !== "object" || value === null || Array.isArray(value)) {
        throw (0, errors_1.createUsageError)("Release manifest contains an invalid supported platform entry.", [
            "Reason: malformed-manifest-supported-platforms",
            "Recovery: regenerate the manifest from release platform metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    const platform = value;
    const os = platform.os;
    const label = platform.label;
    const architectures = platform.architectures;
    const installer = platform.installer;
    if (!["darwin", "linux", "win32"].includes(String(os)) ||
        !["macOS", "Linux", "Windows"].includes(String(label)) ||
        !Array.isArray(architectures) ||
        !architectures.every((architecture) => architecture === "x64" || architecture === "arm64") ||
        !["posix", "powershell"].includes(String(installer))) {
        throw (0, errors_1.createUsageError)("Release manifest supported platform metadata is missing required fields.", [
            "Reason: malformed-manifest-supported-platforms",
            "Recovery: regenerate the manifest from release platform metadata.",
            "Exit behavior: exits before successful-release state is recorded."
        ]);
    }
    return {
        os: os,
        label: label,
        architectures: architectures,
        installer: installer
    };
}
function validateReleaseManifestSupportedPlatforms(platforms) {
    const expectedPlatforms = createReleaseManifestSupportedPlatforms();
    for (const expected of expectedPlatforms) {
        const platform = platforms.find((candidate) => candidate.os === expected.os);
        if (platform === undefined ||
            platform.label !== expected.label ||
            platform.installer !== expected.installer ||
            !expected.architectures.every((architecture) => platform.architectures.includes(architecture))) {
            throw (0, errors_1.createUsageError)("Release manifest supported platform metadata is incomplete.", [
                "Reason: manifest-supported-platforms-incomplete",
                `Expected platform: ${expected.os}`,
                "Recovery: regenerate the manifest from release platform metadata.",
                "Exit behavior: exits before successful-release state is recorded."
            ]);
        }
    }
}
function validateInstallerPayloadShims(options) {
    const requiredShimPaths = options.target === "macos-linux"
        ? [`bin/${options.installedCommandName}`]
        : [`bin/${options.installedCommandName}.cmd`, `bin/${options.installedCommandName}.ps1`];
    for (const shimPath of requiredShimPaths) {
        const fullPath = node_path_1.default.join(options.payloadDirectory, ...shimPath.split("/"));
        if (!node_fs_1.default.existsSync(fullPath) || !node_fs_1.default.statSync(fullPath).isFile()) {
            throw (0, errors_1.createUsageError)("Release installer validation requires executable launch shims before replacement.", [
                "Reason: missing-executable-shim",
                `Missing shim: ${shimPath}`,
                "Recovery: regenerate archive payloads with the configured launcher or shim files.",
                "Exit behavior: exits before existing installation replacement."
            ]);
        }
        if (options.target === "macos-linux" && (node_fs_1.default.statSync(fullPath).mode & 0o111) === 0) {
            throw (0, errors_1.createUsageError)("Release installer validation requires an executable POSIX launcher before replacement.", [
                "Reason: missing-executable-shim",
                `Missing executable bit: ${shimPath}`,
                "Recovery: regenerate archive payloads with an executable POSIX launcher.",
                "Exit behavior: exits before existing installation replacement."
            ]);
        }
    }
}
function isNodeVersionAtLeast20(value) {
    const match = value.trim().match(/^v?(\d+)\.(\d+)\.(\d+)(?:[.-].*)?$/);
    if (match === null) {
        return false;
    }
    const major = Number(match[1]);
    const minor = Number(match[2]);
    const patch = Number(match[3]);
    return Number.isInteger(major) &&
        Number.isInteger(minor) &&
        Number.isInteger(patch) &&
        major >= 20;
}
function snapshotDirectory(directoryPath) {
    if (!node_fs_1.default.existsSync(directoryPath)) {
        return [];
    }
    return collectFiles(directoryPath)
        .map((filePath) => ({
        path: toRepositoryRelativePath(directoryPath, filePath),
        contents: node_fs_1.default.readFileSync(filePath).toString("base64")
    }))
        .sort((left, right) => left.path.localeCompare(right.path));
}
function directoriesMatchSnapshot(directoryPath, snapshot) {
    const currentSnapshot = snapshotDirectory(directoryPath);
    return JSON.stringify(currentSnapshot) === JSON.stringify(snapshot);
}
function isUsageErrorLike(value) {
    return typeof value === "object" &&
        value !== null &&
        "code" in value &&
        value.code === "USAGE_ERROR" &&
        Array.isArray(value.details);
}
function normalizePayloadPath(filePath) {
    return filePath.split(node_path_1.default.sep).join("/").replace(/^\.\/+/, "");
}
function collectFiles(directoryPath) {
    const files = [];
    for (const entry of node_fs_1.default.readdirSync(directoryPath, { withFileTypes: true })) {
        const entryPath = node_path_1.default.join(directoryPath, entry.name);
        if (entry.isDirectory()) {
            files.push(...collectFiles(entryPath));
        }
        else if (entry.isFile()) {
            files.push(entryPath);
        }
    }
    return files;
}
function normalizeStringRecord(value) {
    if (typeof value !== "object" || value === null || Array.isArray(value)) {
        return {};
    }
    return Object.fromEntries(Object.entries(value)
        .filter((entry) => typeof entry[1] === "string"));
}
function readOptionalString(value) {
    return typeof value === "string" ? value : null;
}
function toRepositoryRelativePath(repositoryRoot, value) {
    const relative = node_path_1.default.relative(repositoryRoot, value);
    return relative.split(node_path_1.default.sep).join("/");
}
function listReleaseDirectoryEntries(state) {
    if (!node_fs_1.default.existsSync(state.releaseRoot)) {
        return [];
    }
    return node_fs_1.default.readdirSync(state.releaseRoot, { withFileTypes: true })
        .filter((entry) => entry.isDirectory())
        .map((entry) => {
        const entryPath = node_path_1.default.join(state.releaseRoot, entry.name);
        const markerPath = node_path_1.default.join(entryPath, exports.RELEASE_SUCCESS_MARKER_FILE);
        return {
            name: entry.name,
            path: entryPath,
            relativePath: `${state.releaseRootRelative}/${entry.name}`,
            marker: readReleaseSuccessMarker({
                releaseRoot: state.releaseRoot,
                releaseRootRelative: state.releaseRootRelative,
                versionDirectory: entryPath,
                versionDirectoryRelative: `${state.releaseRootRelative}/${entry.name}`,
                stagingDirectory: node_path_1.default.join(entryPath, "staging"),
                stagingDirectoryRelative: `${state.releaseRootRelative}/${entry.name}/staging`,
                successMarkerPath: markerPath,
                successMarkerPathRelative: `${state.releaseRootRelative}/${entry.name}/${exports.RELEASE_SUCCESS_MARKER_FILE}`
            })
        };
    });
}
function compareStableVersionsDescending(left, right) {
    const parsedLeft = (0, version_1.parseReleaseVersion)(left);
    const parsedRight = (0, version_1.parseReleaseVersion)(right);
    if (!parsedLeft.ok && !parsedRight.ok) {
        return right.localeCompare(left);
    }
    if (!parsedLeft.ok) {
        return 1;
    }
    if (!parsedRight.ok) {
        return -1;
    }
    return parsedRight.major - parsedLeft.major ||
        parsedRight.minor - parsedLeft.minor ||
        parsedRight.patch - parsedLeft.patch;
}
