Files
agenticSystem/mcp-server/tools/media/generateImage.js
2026-04-17 20:03:02 +00:00

100 lines
4.6 KiB
JavaScript

import { z } from "zod";
import axios from "axios";
import fs from "fs";
import path from "path";
import { withAuth } from "../../auth/index.js";
import { handleToolError } from "../helpers/errorHandler.js";
import { withAuthParams } from "../helpers/authSchema.js";
import { pythonPost } from "../helpers/pythonServerClient.js";
import { resolveCurrentProjectDir } from "../files/helpers.js";
// --- Verificacion de creditos ---
const WS_BASE = "https://ws.cocosolution.com/api/handler_acaicode.php";
function getAcaiToken() {
const projectDir = resolveCurrentProjectDir();
if (!projectDir) return null;
try {
const acaiFile = path.join(projectDir, ".acai");
const data = JSON.parse(fs.readFileSync(acaiFile, "utf-8"));
return data.token || null;
} catch { return null; }
}
async function checkCredits() {
const token = getAcaiToken();
if (!token) return false; // Si no hay token, no bloquear
const testParam = process.env.STRIPE_MODE === "test" ? "&test" : "";
try {
const resp = await axios.put(`${WS_BASE}?action=getUsageLimits${testParam}`, {}, {
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
timeout: 10000,
});
return resp.data?.data?.exceeded === true;
} catch { return false; }
}
export function registerGenerateImageTool(server) {
server.tool(
"generate_image",
`Generate an AI image and save it to the project's uploads folder. Returns preview URLs plus the recommended upload URL for upload_record_image. In Forge environments, prefer uploadUrl (or fullUrl if uploadUrl is absent) over dockerUrl when assigning the image to a record field.`,
withAuthParams({
prompt: z.string().describe("Description of the image to generate"),
width: z.number().optional().describe("Image width in pixels (default: 1024)"),
height: z.number().optional().describe("Image height in pixels (default: 1024)"),
style: z.string().optional().describe("Image style hint to add to prompt (e.g., 'photographic', 'digital-art', 'minimalist')"),
fileName: z.string().optional().describe("Custom filename (without extension). If not provided, auto-generated."),
}),
{ readOnlyHint: false, destructiveHint: false },
withAuth(async ({ prompt, width = 1024, height = 1024, style, fileName }, extra) => {
try {
// Verificar creditos antes de generar
const exceeded = await checkCredits();
if (exceeded) {
return {
content: [{ type: "text", text: "Error: No te quedan creditos. Mejora tu plan para seguir usando el asistente." }],
isError: true,
};
}
const projectSlug = path.basename(resolveCurrentProjectDir());
const safeFileName = fileName || `generated-${Date.now()}`;
const destRelativePath = `cms/uploads/generated/${safeFileName}.jpg`;
const fullPrompt = style ? `${style} style: ${prompt}` : prompt;
let result;
try {
result = await pythonPost("/api/generate-image", {
project: projectSlug,
prompt: fullPrompt,
destRelativePath,
}, 180000); // 3min timeout para generacion IA
} catch (pyErr) {
return handleToolError(new Error(`Python generate-image failed: ${pyErr.response?.data?.error || pyErr.message}`), 'generate_image', { prompt });
}
if (!result?.success) {
return { content: [{ type: "text", text: JSON.stringify({ error: result?.error || "Generation failed" }, null, 2) }], isError: true };
}
return {
content: [{
type: "text",
text: JSON.stringify({
success: true,
message: `Image generated and saved to ${result.relativePath}`,
uploadUrl: result.fullUrl || result.dockerUrl,
fullUrl: result.fullUrl || result.dockerUrl,
relativePath: result.relativePath,
fileName: result.fileName,
size: result.size,
}, null, 2),
}],
};
} catch (error) {
return handleToolError(error, "generate_image", { prompt });
}
})
);
}