import fs from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' import { validateAssetBuildManifest } from './asset-build-manifest.mjs' import { resolvePythonExecutable, runPythonCommand } from './python-runtime.mjs' const scriptDirectory = path.dirname(fileURLToPath(import.meta.url)) const pipelineDirectory = path.resolve(scriptDirectory, '..') const workspace = path.resolve(pipelineDirectory, '..') const argument = process.argv[2] if (!argument) throw new Error('用法:node build-raster-assets.mjs <仓库相对清单路径>') // 清单参数本身也必须留在工作区;source/output 的边界由 schema validator 逐项负责。 const manifestPath = path.resolve(workspace, argument) const relativeManifest = path.relative(workspace, manifestPath) if (relativeManifest.startsWith('..') || path.isAbsolute(relativeManifest)) { throw new Error(`构建清单越出工作区:${argument}`) } const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) validateAssetBuildManifest(manifest, workspace) const supportedModes = new Set(['opaque-resize', 'opaque-cover-crop', 'warm-gold-frame-extract']) for (const asset of manifest.assets) { if (!supportedModes.has(asset.processing.mode)) { throw new Error(`通用 raster 构建器不支持模式:${asset.processing.mode}`) } } const python = resolvePythonExecutable({ pipelineDirectory }) const reportPath = path.join(pipelineDirectory, 'generated', manifest.family, 'quality-report.json') // Node 只编排严格 schema、锁定的 Python 入口和质量报告;像素算法只存在于 Python。 runPythonCommand({ executable: python, args: [path.join(scriptDirectory, 'build_raster_assets.py'), manifestPath, '--workspace', workspace], cwd: workspace, }) runPythonCommand({ executable: python, args: [path.join(scriptDirectory, 'verify-assets.py'), manifestPath, '--workspace', workspace, '--report', reportPath], cwd: workspace, })