| 239 | |
| 240 | /** Roll the "current" pointer back to an earlier (existing) version — no new file. */ |
| 241 | export async function rollbackArtifact(cwd: string, id: string, toVersion: number, write: WriteFn, now = new Date().toISOString()): Promise<ArtifactManifest> { |
| 242 | // Lock so a concurrent updateArtifact can't slip a new version in between our |
| 243 | // read and write (which would drop that version when we save the manifest). |
| 244 | return withLock(manifestLockPath(cwd, id), async () => { |
| 245 | const manifest = await readManifest(cwd, id); |
| 246 | if (!manifest) throw new Error(`Artifact "${id}" not found.`); |
| 247 | if (!manifest.versions.some(v => v.v === toVersion)) throw new Error(`Artifact "${id}" has no version ${toVersion}.`); |
| 248 | const updated: ArtifactManifest = { ...manifest, current: toVersion, updatedAt: now }; |
| 249 | await writeFileAtomic(manifestPath(cwd, id), JSON.stringify(updated, null, 2)); |
| 250 | return updated; |
| 251 | }); |
| 252 | } |