Files
mintel.me/apps/web/scripts/ai-estimate.ts
Marc Mintel 905ce98bc4
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 6s
Build & Deploy / 🧪 QA (push) Failing after 54s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 1s
chore: align deployment pipeline with klz-2026 standards
- Add branch deployment support

- Switch build platform to linux/amd64

- Extract checks to turbo pipeline

- Add pre/post-deploy scripts & cms-sync
2026-03-01 00:41:38 +01:00

211 lines
6.6 KiB
TypeScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import * as path from "node:path";
import * as fs from "node:fs/promises";
import { existsSync } from "node:fs";
import { config as dotenvConfig } from "dotenv";
import { ConceptPipeline } from "@mintel/concept-engine";
import { EstimationPipeline } from "@mintel/estimation-engine";
import { PdfEngine } from "@mintel/pdf/server";
import { fileURLToPath } from "node:url";
import { createRequire } from "node:module";
dotenvConfig({ path: path.resolve(process.cwd(), "../../.env") });
dotenvConfig({ path: path.resolve(process.cwd(), ".env") });
try {
const require = createRequire(import.meta.url);
const conceptPkg = require.resolve("@mintel/concept-engine");
const atMintelEnv = path.resolve(path.dirname(conceptPkg), "../../../.env");
if (existsSync(atMintelEnv)) {
dotenvConfig({ path: atMintelEnv });
}
} catch {
/* @mintel/concept-engine not resolvable — skip */
}
async function main() {
const OPENROUTER_KEY =
process.env.OPENROUTER_API_KEY || process.env.OPENROUTER_KEY;
if (!OPENROUTER_KEY) {
console.error("❌ Error: OPENROUTER_API_KEY not found in environment.");
process.exit(1);
}
const args = process.argv.slice(2);
const briefingArg = args[0];
if (!briefingArg) {
console.error(
"❌ Error: Provide a briefing file or text as the first argument.",
);
process.exit(1);
}
let targetUrl = "";
let budget = "";
let comments = "";
const clearCache = args.includes("--clear-cache");
for (let i = 1; i < args.length; i++) {
if (args[i] === "--url") targetUrl = args[++i];
if (args[i] === "--budget") budget = args[++i];
if (args[i] === "--comments") comments = args[++i];
}
let briefing = briefingArg;
if (briefing.startsWith("@")) {
const rawPath = briefing.substring(1);
const filePath = rawPath.startsWith("/")
? rawPath
: path.resolve(process.cwd(), rawPath);
if (existsSync(filePath)) {
briefing = await fs.readFile(filePath, "utf8");
console.log(`📄 Loaded briefing from: ${filePath}`);
} else {
console.error(`❌ Briefing file not found: ${filePath}`);
process.exit(1);
}
}
if (!targetUrl) {
const urlMatch = briefing.match(/https?:\/\/[^\s]+/);
if (urlMatch) targetUrl = urlMatch[0];
}
const monorepoRoot = path.resolve(process.cwd(), "../../");
const crawlDir = path.join(
path.resolve(monorepoRoot, "../at-mintel"),
"data/crawls",
);
const outputDir = path.join(monorepoRoot, "out");
const konzeptDir = path.join(outputDir, "konzept");
const schaetzungDir = path.join(outputDir, "schaetzung");
const agbDir = path.join(outputDir, "agb");
const infoDir = path.join(outputDir, "info");
const deckblattDir = path.join(outputDir, "deckblatt");
const abschlussDir = path.join(outputDir, "abschluss");
await fs.mkdir(outputDir, { recursive: true });
await fs.mkdir(konzeptDir, { recursive: true });
await fs.mkdir(schaetzungDir, { recursive: true });
await fs.mkdir(agbDir, { recursive: true });
await fs.mkdir(infoDir, { recursive: true });
await fs.mkdir(deckblattDir, { recursive: true });
await fs.mkdir(abschlussDir, { recursive: true });
const conceptPipeline = new ConceptPipeline({
openrouterKey: OPENROUTER_KEY,
zyteApiKey: process.env.ZYTE_API_KEY,
outputDir,
crawlDir,
});
const engine = new PdfEngine() as any;
const headerIcon = path.join(
monorepoRoot,
"apps/web/src/assets/logo/Icon-Black-Transparent.png",
);
const footerLogo = path.join(
monorepoRoot,
"apps/web/src/assets/logo/Logo-Black-Transparent.png",
);
try {
const conceptResult = await conceptPipeline.run({
briefing,
url: targetUrl,
comments,
clearCache,
});
const companyName = conceptResult.auditedFacts?.companyName || "unknown";
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
console.log("\n📄 Generating Concept PDF...");
const conceptPdfPath = path.join(
konzeptDir,
`${companyName}_Konzept_${timestamp}.pdf`,
);
await engine.generateConceptPdf(conceptResult, conceptPdfPath, {
headerIcon,
footerLogo,
});
console.log(`✅ Created Concept PDF at: ${conceptPdfPath}`);
console.log("\n==================================================");
console.log("💰 PART 2: ESTIMATION ENGINE");
console.log("==================================================");
const estimationPipeline = new EstimationPipeline({
openrouterKey: OPENROUTER_KEY,
outputDir,
crawlDir: "",
});
const estimationResult = await estimationPipeline.run({
concept: conceptResult,
budget,
});
if (estimationResult.formState) {
console.log("\n📄 Generating Estimation PDF...");
const estimationPdfPath = path.join(
schaetzungDir,
`${companyName}_Angebot_${timestamp}.pdf`,
);
await engine.generateEstimatePdf(
estimationResult.formState,
estimationPdfPath,
{ headerIcon, footerLogo },
);
console.log(`✅ Created Angebot PDF at: ${estimationPdfPath}`);
console.log("\n📄 Generating AGBs PDF...");
const agbPdfPath = path.join(
agbDir,
`${companyName}_AGBs_${timestamp}.pdf`,
);
await engine.generateAgbsPdf(agbPdfPath, { headerIcon, footerLogo });
console.log(`✅ Created AGBs PDF at: ${agbPdfPath}`);
console.log("\n📄 Generating Deckblatt PDF...");
const deckblattPdfPath = path.join(
deckblattDir,
`${companyName}_Deckblatt_${timestamp}.pdf`,
);
await engine.generateFrontPagePdf(
estimationResult.formState,
deckblattPdfPath,
{ headerIcon },
);
console.log(`✅ Created Deckblatt PDF at: ${deckblattPdfPath}`);
console.log("\n📄 Generating Abschluss PDF...");
const abschlussPdfPath = path.join(
abschlussDir,
`${companyName}_Abschluss_${timestamp}.pdf`,
);
await engine.generateClosingPdf(abschlussPdfPath, {
headerIcon,
footerLogo,
});
console.log(`✅ Created Abschluss PDF at: ${abschlussPdfPath}`);
} else {
console.log("\n⚠ No formState generated, skipping Estimation PDF.");
}
// Generate Info PDF
console.log("\n📄 Generating Arbeitsweise PDF...");
const infoPdfPath = path.join(
infoDir,
`${companyName}_Arbeitsweise_${timestamp}.pdf`,
);
await engine.generateInfoPdf(infoPdfPath, { headerIcon, footerLogo });
console.log(`✅ Created Arbeitsweise PDF at: ${infoPdfPath}`);
} catch (e) {
console.error(`\n❌ Pipeline failed: ${(e as Error).message}`);
process.exit(1);
}
}
main();