#!/usr/bin/env node // Temporarily removes private workspace dependencies from the published manifest. import { existsSync } from "node:fs"; import { mkdir, readFile, rm, writeFile } from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; const PACKAGE_JSON_PATH = "package.json"; const BACKUP_PATH = path.join(".artifacts", "package-manifest", "package.json.prepack-backup"); function preparedPackageManifest(content) { const packageJson = JSON.parse(content); const devDependencies = packageJson.devDependencies; if (!devDependencies || typeof devDependencies !== "object" || Array.isArray(devDependencies)) { return content; } const devDependencyEntries = Object.entries(devDependencies); const publishedDevDependencyEntries = devDependencyEntries.filter( ([, spec]) => typeof spec !== "string" || !spec.startsWith("workspace:"), ); if (publishedDevDependencyEntries.length === devDependencyEntries.length) { return content; } if (publishedDevDependencyEntries.length === 0) { delete packageJson.devDependencies; } else { packageJson.devDependencies = Object.fromEntries(publishedDevDependencyEntries); } return `${JSON.stringify(packageJson, null, 2)}\n`; } /** Restore package.json after prepack removed private workspace dependencies. */ export async function restorePackageManifest(cwd = process.cwd()) { const backupPath = path.join(cwd, BACKUP_PATH); if (!existsSync(backupPath)) { return false; } const packageJsonPath = path.join(cwd, PACKAGE_JSON_PATH); const [original, current] = await Promise.all([ readFile(backupPath, "utf8"), readFile(packageJsonPath, "utf8"), ]); if (current !== original && current !== preparedPackageManifest(original)) { throw new Error( `Refusing to restore ${PACKAGE_JSON_PATH} because it changed after prepack sanitized it.`, ); } await writeFile(packageJsonPath, original, "utf8"); await rm(backupPath, { force: true }); return true; } /** Remove private workspace dependencies while recording restorable source bytes. */ export async function preparePackageManifest(cwd = process.cwd()) { const packageJsonPath = path.join(cwd, PACKAGE_JSON_PATH); const backupPath = path.join(cwd, BACKUP_PATH); const original = await readFile(packageJsonPath, "utf8"); const prepared = preparedPackageManifest(original); if (prepared === original) { return false; } await mkdir(path.dirname(backupPath), { recursive: true }); try { await writeFile(backupPath, original, { encoding: "utf8", flag: "wx" }); } catch (error) { if (error?.code === "EEXIST") { throw new Error( `Another package preparation owns ${PACKAGE_JSON_PATH}; wait for it to finish or run \`node scripts/openclaw-postpack.mjs\` after an interrupted pack.`, { cause: error }, ); } throw error; } try { await writeFile(packageJsonPath, prepared, "utf8"); } catch (error) { try { await restorePackageManifest(cwd); } catch (restoreError) { const failure = new Error( `Writing ${PACKAGE_JSON_PATH} failed and its source state could not be restored.`, { cause: error }, ); Object.assign(failure, { restoreError }); throw failure; } throw error; } return true; } async function main(argv = process.argv.slice(2)) { if (argv.length !== 1 || (argv[0] !== "prepare" && argv[0] !== "restore")) { console.error("Usage: node scripts/package-manifest.mjs "); process.exitCode = 1; return; } const changed = argv[0] === "prepare" ? await preparePackageManifest() : await restorePackageManifest(); console.error( changed ? `package-manifest: ${argv[0] === "prepare" ? "sanitized" : "restored"} package.json.` : `package-manifest: no ${argv[0] === "prepare" ? "sanitation" : "cleanup"} needed.`, ); } if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { await main(); }