import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { fileURLToPath } from "node:url"; import test from "node:test"; const syncVersionsScript = fileURLToPath(new URL("./sync-versions.js", import.meta.url)); async function writeManifest(root, relativeDirectory, manifest) { const directory = join(root, relativeDirectory); await mkdir(directory, { recursive: true }); await writeFile(join(directory, "package.json"), `${JSON.stringify(manifest, null, "\t")}\n`); } async function readManifest(root, relativeDirectory) { return JSON.parse(await readFile(join(root, relativeDirectory, "package.json"), "utf8")); } function runSyncVersions(root) { return spawnSync(process.execPath, [syncVersionsScript, join(root, "packages")], { cwd: root, encoding: "utf8", }); } // #10865: caret ranges allowed older agents to install incompatible newer workspace dependencies. test("pins published and private dependencies without touching registry aliases, generated manifests, or published lockstep", async () => { const root = await mkdtemp(join(tmpdir(), "pi-sync-versions-")); try { await writeManifest(root, "packages/ai", { name: "@earendil-works/pi-ai", version: "2.0.0", }); await writeManifest(root, "packages/coding-agent", { name: "@earendil-works/pi-coding-agent", version: "2.0.0", dependencies: { "@earendil-works/pi-ai": "^2.0.0", }, devDependencies: { "@earendil-works/pi-ai": "~1.0.0", }, optionalDependencies: { "@earendil-works/pi-ai": "1.0.0", }, }); await writeManifest(root, "packages/evals", { name: "@earendil-works/pi-evals", version: "9.9.9", private: true, dependencies: { "@earendil-works/pi-coding-agent": "^1.0.0", "@mariozechner/pi-ai": "npm:@earendil-works/pi-ai@1.0.0", }, }); await writeManifest(root, "packages/coding-agent/install-lock", { name: "generated-install-lock", version: "0.0.0", private: true, dependencies: { "@earendil-works/pi-coding-agent": "^1.0.0", }, }); const result = runSyncVersions(root); assert.equal(result.status, 0, result.stderr); const agentManifest = await readManifest(root, "packages/coding-agent"); for (const section of ["dependencies", "devDependencies", "optionalDependencies"]) { assert.equal(agentManifest[section]["@earendil-works/pi-ai"], "2.0.0"); } const evalsManifest = await readManifest(root, "packages/evals"); assert.equal(evalsManifest.dependencies["@earendil-works/pi-coding-agent"], "2.0.0"); assert.equal(evalsManifest.dependencies["@mariozechner/pi-ai"], "npm:@earendil-works/pi-ai@1.0.0"); const generatedManifest = await readManifest(root, "packages/coding-agent/install-lock"); assert.equal(generatedManifest.dependencies["@earendil-works/pi-coding-agent"], "^1.0.0"); const alreadySynced = runSyncVersions(root); assert.equal(alreadySynced.status, 0, alreadySynced.stderr); assert.match(alreadySynced.stdout, /All inter-package dependencies are already in sync/); await writeManifest(root, "packages/ai", { name: "@earendil-works/pi-ai", version: "3.0.0", }); const lockstepFailure = runSyncVersions(root); assert.equal(lockstepFailure.status, 1, lockstepFailure.stderr); } finally { await rm(root, { recursive: true, force: true }); } });