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:
Apple
2026-01-28 06:40:34 -08:00
parent 4aeb69e7ae
commit 0c8bef82f4
120 changed files with 21905 additions and 425 deletions

View 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);
});