import { readFile, writeFile } from 'node:fs/promises'; import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; const ROOT = fileURLToPath(new URL('..', import.meta.url)); const AVATAR_DIRECTORY = join(ROOT, 'assets', 'chickpea-avatars', 'agent-defaults'); const OUTPUT = join( ROOT, 'src', 'slack', 'agent-presence', 'default-avatar-pool.generated.ts', ); const PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]); const AVATAR_FILES = [ '01-sage.png', '02-coral.png', '03-lilac.png', '04-mineral-blue.png', '05-dusty-rose.png', '06-deep-moss.png', '07-periwinkle.png', '08-seafoam.png', '09-warm-oat.png', '10-midnight-indigo.png', '11-apricot.png', '12-muted-berry.png', ]; for (const file of AVATAR_FILES) { const bytes = await readFile(join(AVATAR_DIRECTORY, file)); if (!bytes.subarray(0, PNG_SIGNATURE.length).equals(PNG_SIGNATURE)) { throw new Error(`${file} is not a PNG`); } if (bytes.subarray(12, 16).toString('ascii') !== 'IHDR') { throw new Error(`${file} has no PNG IHDR`); } const width = bytes.readUInt32BE(16); const height = bytes.readUInt32BE(20); if (width !== 512 || height !== 512) { throw new Error(`${file} must be exactly 512x512, got ${width}x${height}`); } if (bytes.byteLength > 512 * 1_024) { throw new Error(`${file} exceeds Slack's 512 KiB avatar budget`); } } const source = `// Generated by scripts/build-agent-avatar-pool.mjs. Do not edit by hand.\n` + `export const DEFAULT_AGENT_AVATAR_FILES = ${JSON.stringify(AVATAR_FILES, null, 2)} as const;\n`; await writeFile(OUTPUT, source); console.log(`Wrote ${AVATAR_FILES.length} Agent avatars to ${OUTPUT}`);