feat: Add Alateya, Clan, Eonarch agents + fix gateway-router connection
## Agents Added - Alateya: R&D, biotech, innovations - Clan (Spirit): Community spirit agent - Eonarch: Consciousness evolution agent ## Changes - docker-compose.node1.yml: Added tokens for all 3 new agents - gateway-bot/http_api.py: Added configs and webhook endpoints - gateway-bot/clan_prompt.txt: New prompt file - gateway-bot/eonarch_prompt.txt: New prompt file ## Fixes - Fixed ROUTER_URL from :9102 to :8000 (internal container port) - All 9 Telegram agents now working ## Documentation - Created PROJECT-MASTER-INDEX.md - single entry point - Added various status documents and scripts Tokens configured: - Helion, NUTRA, Agromatrix (existing) - Alateya, Clan, Eonarch (new) - Druid, GreenFood, DAARWIZZ (configured)
This commit is contained in:
119
services/render-pptx-worker/index.js
Normal file
119
services/render-pptx-worker/index.js
Normal file
@@ -0,0 +1,119 @@
|
||||
import fs from "node:fs";
|
||||
import crypto from "node:crypto";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
import axios from "axios";
|
||||
import { connect } from "nats";
|
||||
import { Client as Minio } from "minio";
|
||||
import PptxGenJS from "pptxgenjs";
|
||||
|
||||
const NATS_URL = process.env.NATS_URL || "nats://nats:4222";
|
||||
const REGISTRY_URL = (process.env.ARTIFACT_REGISTRY_URL || "http://artifact-registry:9220").replace(/\/$/, "");
|
||||
const MINIO_ENDPOINT = process.env.MINIO_ENDPOINT || "minio:9000";
|
||||
const MINIO_ACCESS_KEY = process.env.MINIO_ACCESS_KEY || "minioadmin";
|
||||
const MINIO_SECRET_KEY = process.env.MINIO_SECRET_KEY || "minioadmin";
|
||||
const MINIO_BUCKET = process.env.MINIO_BUCKET || "artifacts";
|
||||
const MINIO_SECURE = (process.env.MINIO_SECURE || "false").toLowerCase() === "true";
|
||||
|
||||
const minioClient = new Minio({
|
||||
endPoint: MINIO_ENDPOINT.split(":")[0],
|
||||
port: Number(MINIO_ENDPOINT.split(":")[1] || 9000),
|
||||
useSSL: MINIO_SECURE,
|
||||
accessKey: MINIO_ACCESS_KEY,
|
||||
secretKey: MINIO_SECRET_KEY,
|
||||
});
|
||||
|
||||
const toBuffer = async (stream) => {
|
||||
const chunks = [];
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
return Buffer.concat(chunks);
|
||||
};
|
||||
|
||||
const sha256 = (buf) => crypto.createHash("sha256").update(buf).digest("hex");
|
||||
|
||||
const renderPptx = async (slidespec, outPath) => {
|
||||
const pptx = new PptxGenJS();
|
||||
pptx.layout = "LAYOUT_WIDE";
|
||||
pptx.author = "DAARION Artifact Worker";
|
||||
|
||||
const slides = slidespec.slides || [];
|
||||
if (!slides.length) {
|
||||
throw new Error("No slides in slidespec");
|
||||
}
|
||||
|
||||
slides.forEach((slide, idx) => {
|
||||
const s = pptx.addSlide();
|
||||
const title = slide.title || slidespec.title || `Slide ${idx + 1}`;
|
||||
s.addText(title, { x: 0.6, y: 0.6, w: 12, h: 0.8, fontSize: idx === 0 ? 36 : 28, bold: true });
|
||||
|
||||
if (slide.subtitle) {
|
||||
s.addText(slide.subtitle, { x: 0.6, y: 1.6, w: 12, h: 0.5, fontSize: 16, color: "666666" });
|
||||
}
|
||||
|
||||
if (slide.bullets && Array.isArray(slide.bullets) && slide.bullets.length) {
|
||||
s.addText(slide.bullets.map((b) => ({ text: b, options: { bullet: { indent: 18 } } })), {
|
||||
x: 0.8,
|
||||
y: 2.0,
|
||||
w: 11.5,
|
||||
h: 4.5,
|
||||
fontSize: 18,
|
||||
color: "333333",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await pptx.writeFile({ fileName: outPath });
|
||||
};
|
||||
|
||||
const handleJob = async (msg) => {
|
||||
const data = JSON.parse(msg.data.toString());
|
||||
const { job_id, storage_key, theme_id, artifact_id, input_version_id } = data;
|
||||
const slidespecKey = storage_key || `artifacts/${artifact_id}/versions/${input_version_id}/slidespec.json`;
|
||||
|
||||
try {
|
||||
const objectStream = await minioClient.getObject(MINIO_BUCKET, slidespecKey);
|
||||
const buf = await toBuffer(objectStream);
|
||||
const slidespec = JSON.parse(buf.toString("utf-8"));
|
||||
|
||||
const tmpFile = path.join(os.tmpdir(), `${job_id}.pptx`);
|
||||
await renderPptx(slidespec, tmpFile, theme_id);
|
||||
|
||||
const pptxBuf = fs.readFileSync(tmpFile);
|
||||
const pptxSha = sha256(pptxBuf);
|
||||
const pptxKey = `artifacts/${artifact_id}/versions/${input_version_id}/presentation.pptx`;
|
||||
|
||||
await minioClient.putObject(MINIO_BUCKET, pptxKey, pptxBuf, pptxBuf.length, {
|
||||
"Content-Type": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
});
|
||||
|
||||
await axios.post(`${REGISTRY_URL}/jobs/${job_id}/complete`, {
|
||||
output_storage_key: pptxKey,
|
||||
mime: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
size_bytes: pptxBuf.length,
|
||||
sha256: pptxSha,
|
||||
label: "pptx",
|
||||
});
|
||||
|
||||
fs.unlinkSync(tmpFile);
|
||||
} catch (err) {
|
||||
await axios.post(`${REGISTRY_URL}/jobs/${job_id}/fail`, {
|
||||
error_text: String(err?.message || err),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const main = async () => {
|
||||
const nc = await connect({ servers: [NATS_URL] });
|
||||
const sub = nc.subscribe("artifact.job.render_pptx.requested");
|
||||
for await (const msg of sub) {
|
||||
await handleJob(msg);
|
||||
}
|
||||
};
|
||||
|
||||
main().catch((err) => {
|
||||
console.error("worker failed", err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user