Files

123 lines
3.9 KiB
JavaScript

const fs = require("fs");
const path = require("path");
const { spawnSync } = require("child_process");
const { buildPipeline, detectPatterns, getPattern } = require("./pipeline-config");
const { collectMetricsFromFile } = require("./collect-residue-metrics");
function sanitizeStepName(name) {
return String(name).replace(/[^a-z0-9_]+/gi, "_");
}
function runStep(nodePath, step, currentPath, nextPath) {
const start = Date.now();
const result = spawnSync(nodePath, [step.scriptPath, currentPath, nextPath], {
encoding: "utf8",
env: process.env,
timeout: step.timeoutMs
});
const durationMs = Date.now() - start;
const stdout = result.stdout ? result.stdout.trim() : "";
const stderr = result.stderr ? result.stderr.trim() : "";
const timedOut = Boolean(result.error && result.error.code === "ETIMEDOUT");
return {
id: step.id,
script: step.script,
outputFile: path.basename(nextPath),
timeoutMs: step.timeoutMs,
durationMs,
status: timedOut ? "timed_out" : result.status === 0 ? "ok" : "failed",
exitCode: typeof result.status === "number" ? result.status : null,
stdout,
stderr,
error: result.error ? String(result.error.message || result.error) : ""
};
}
function copyFile(fromPath, toPath) {
fs.copyFileSync(fromPath, toPath);
}
function ensureDir(dirPath) {
fs.mkdirSync(dirPath, { recursive: true });
}
function main() {
const [, , inputPath, outputDir, hint = ""] = process.argv;
if (!inputPath || !outputDir) {
console.error("Usage: node run-pipeline.js <input.js> <output-dir> [hint]");
process.exit(1);
}
const absoluteInput = path.resolve(inputPath);
const absoluteOutputDir = path.resolve(outputDir);
ensureDir(absoluteOutputDir);
const sourceText = fs.readFileSync(absoluteInput, "utf8");
const detection = detectPatterns({
inputPath: absoluteInput,
hint,
sourceText
});
const pattern = getPattern(detection.bestId);
const pipeline = buildPipeline(pattern.id);
const report = {
inputPath: absoluteInput,
outputDir: absoluteOutputDir,
hint,
selectedPattern: {
id: pattern.id,
displayName: pattern.displayName,
reference: pattern.reference,
families: pattern.families,
notes: pattern.notes
},
detections: detection.detections,
steps: [],
status: "ok",
failedStep: null,
lastGoodFile: "00_source.js",
finalMetrics: null
};
const sourceCopyPath = path.join(absoluteOutputDir, "00_source.js");
copyFile(absoluteInput, sourceCopyPath);
let currentPath = sourceCopyPath;
for (let index = 0; index < pipeline.length; index += 1) {
const step = pipeline[index];
const stepName = `${String(index + 1).padStart(2, "0")}_${sanitizeStepName(step.id)}.js`;
const nextPath = path.join(absoluteOutputDir, stepName);
const outcome = runStep(process.execPath, step, currentPath, nextPath);
report.steps.push(outcome);
if (outcome.status !== "ok") {
report.status = outcome.status;
report.failedStep = step.id;
break;
}
currentPath = nextPath;
report.lastGoodFile = stepName;
}
const finalPath = path.join(absoluteOutputDir, "final.js");
copyFile(currentPath, finalPath);
report.finalMetrics = collectMetricsFromFile(finalPath);
fs.writeFileSync(path.join(absoluteOutputDir, "selected-patterns.json"), JSON.stringify({
bestId: detection.bestId,
detections: detection.detections,
selectedPattern: report.selectedPattern
}, null, 2), "utf8");
fs.writeFileSync(path.join(absoluteOutputDir, "pipeline-report.json"), JSON.stringify(report, null, 2), "utf8");
process.stdout.write(`${JSON.stringify({
status: report.status,
selectedPattern: report.selectedPattern.id,
failedStep: report.failedStep,
lastGoodFile: report.lastGoodFile
})}\n`);
}
if (require.main === module) {
main();
}