import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import zlib from 'node:zlib'; import { checkPackage } from '../install.js'; import { resultEnvelope } from '../mcp/src/core/result-envelope.mjs'; import { buildImageSetPlan, verifyPlan } from '../mcp/src/features/planning/planner.mjs'; import { scoreImageSet } from '../skills/fmode-image-set-score/scripts/score-image-set.mjs'; function crc32(bytes) { let crc = 0xffffffff; for (const byte of bytes) { crc ^= byte; for (let bit = 0; bit < 8; bit += 1) crc = (crc >>> 1) ^ (crc & 1 ? 0xedb88320 : 0); } return (crc ^ 0xffffffff) >>> 0; } function pngChunk(type, data = Buffer.alloc(0)) { const typeBytes = Buffer.from(type, 'ascii'); const chunk = Buffer.alloc(12 + data.length); chunk.writeUInt32BE(data.length, 0); typeBytes.copy(chunk, 4); data.copy(chunk, 8); chunk.writeUInt32BE(crc32(Buffer.concat([typeBytes, data])), 8 + data.length); return chunk; } function fakePng(width, height) { const ihdr = Buffer.alloc(13); ihdr.writeUInt32BE(width, 0); ihdr.writeUInt32BE(height, 4); ihdr[8] = 8; ihdr[9] = 2; const raw = Buffer.alloc(height * (width * 3 + 1)); return Buffer.concat([ Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]), pngChunk('IHDR', ihdr), pngChunk('IDAT', zlib.deflateSync(raw)), pngChunk('IEND') ]); } const root = path.resolve(import.meta.dirname, '..'); const checked = checkPackage(root); if (!checked.ok) throw new Error(`Package check failed: ${checked.errors.join(', ')}`); const envelope = resultEnvelope({ status: 'ok', assistantMessage: 'smoke' }); for (const key of ['status','assistantMessage','summary','data','files','nextActions','warnings','errors']) { if (!(key in envelope)) throw new Error(`Missing envelope field: ${key}`); } const plan = buildImageSetPlan({ request: '规划七张商品套图', productDescription: '测试商品' }); verifyPlan(plan); if (plan.items.length !== 7 || plan.requiresConfirmation !== true) throw new Error('Planning smoke failed.'); for (const locale of ['zh', 'en']) { const directory = path.join(root, 'mcp', 'catalog', 'templates', locale); if (fs.readdirSync(directory).filter(name => name.endsWith('.json')).length !== 25) throw new Error(`${locale} template count mismatch`); } const scoreFixture = fs.mkdtempSync(path.join(os.tmpdir(), 'fmode-score-smoke-')); let score; try { fs.writeFileSync(path.join(scoreFixture, 'one.png'), fakePng(1024, 1024)); score = scoreImageSet({ inputs: [scoreFixture], profile: 'generic' }); if (score.automaticScore !== 100 || score.overallScore !== null || score.grade !== null || score.verdict !== 'insufficient_evidence' || score.sourceIntegrity.unchanged !== true) { throw new Error('Independent score smoke failed.'); } } finally { fs.rmSync(scoreFixture, { recursive: true, force: true }); } console.log(JSON.stringify({ status: 'ok', check: checked, planId: plan.planId, templates: 50, score: { version: score.scoreVersion, automatic: score.automaticScore, overall: score.overallScore, verdict: score.verdict } }, null, 2));