| 169 | |
| 170 | /** Create a new artifact: writes v1 file + manifest. Returns the manifest + the file path. */ |
| 171 | export async function createArtifact(cwd: string, input: CreateInput, write: WriteFn, now = new Date().toISOString()): Promise<{ manifest: ArtifactManifest; absFile: string }> { |
| 172 | const id = await uniqueId(cwd, slugifyArtifactId(input.title)); |
| 173 | const rel = path.join('v1', entryFileName(input.type)); |
| 174 | const absFile = path.join(artifactDir(cwd, id), rel); |
| 175 | await write(absFile, input.content); |
| 176 | const manifest = buildManifest(id, input.title, input.type, rel, now, input.note); |
| 177 | // Manifest corruption is the catastrophic case (a truncated manifest makes the |
| 178 | // whole artifact look deleted), so write it atomically regardless of WriteFn. |
| 179 | await writeFileAtomic(manifestPath(cwd, id), JSON.stringify(manifest, null, 2)); |
| 180 | return { manifest, absFile }; |
| 181 | } |
| 182 | |
| 183 | export interface UpdateInput { id: string; content: string; note?: string; } |
| 184 | |