import { z } from "zod"; import axios from "axios"; import { withAuth } from "../../auth/index.js"; import { handleToolError, validateRequired } from "../helpers/errorHandler.js"; import { withAuthParams } from "../helpers/authSchema.js"; import { LOCAL_SERVER_URL } from "../../config/index.js"; import { resolveCurrentProjectDir } from "../files/helpers.js"; import { resolveCurrentAcaiUser } from "../helpers/sessionHelpers.js"; export function registerCreateModuleTool(server) { server.tool( "create_module", `Create a new builder module in the project. This creates the module directory with index-base.tpl, style.css, and script.js, then compiles it automatically. After creating the module, use add_module_to_record to place it on a page, then set_module_config_vars to fill its variables with content. Parameters: - moduleId: unique identifier (lowercase, underscores, e.g. "hero_banner") - html: the Twig/HTML content for index-base.tpl - css: optional CSS for style.css - js: optional JavaScript for script.js - php: optional PHP code for module hook file .php - label: human-readable name (e.g. "Hero Banner V2") - description: brief description of what the module does`, withAuthParams({ moduleId: z.string().describe("Module identifier (lowercase, underscores, e.g. 'hero_banner')"), html: z.string().describe("HTML/Twig content for index-base.tpl ( needed for compile module )"), css: z.string().optional().default("").describe("CSS content for style.css ( optional, you can also add CSS later on file )"), js: z.string().optional().default("").describe("JavaScript content for script.js ( optional, you can also add CSS later on file )"), php: z.string().optional().default("").describe("PHP code for module hook file .php ( optional, you can also add CSS later on file )"), label: z.string().optional().default("").describe("Human-readable module name"), description: z.string().optional().default("").describe("Brief description"), }), { readOnlyHint: false, destructiveHint: false }, withAuth(async ({ moduleId, html, css, js, php, label, description }, extra) => { try { const validationError = validateRequired({ moduleId, html }, ['moduleId', 'html'], 'create_module'); if (validationError) return validationError; const projectDir = resolveCurrentProjectDir(); if (!projectDir) { return { content: [{ type: "text", text: "Error: ACAI_PROJECT_DIR not set" }], isError: true }; } moduleId = moduleId.toLowerCase().replace(/\s+/g, '_'); // Ensure moduleId is lowercase and uses underscores moduleId = moduleId + "_" + (Math.random().toString(36).substring(2, 8).toUpperCase()); // Inyectar X-Acai-User para que el endpoint autenticado del // server Python resuelva rutas dentro de /opt/acai/webs//. const acaiUser = resolveCurrentAcaiUser(); const headers = { "Content-Type": "application/json" }; if (acaiUser) headers["X-Acai-User"] = acaiUser; const response = await axios.post( `${LOCAL_SERVER_URL}/api/create-module`, { project_dir: projectDir, module_id: moduleId, html, css: css || "", js: js || "", label, description, php: php || "" }, { headers, timeout: 30000 } ); if (response.data?.success) { return { content: [{ type: "text", text: JSON.stringify({ success: true, moduleId, path: response.data.path, compiled: response.data.compiled, note: response.data.compiled ? "Module created and compiled. Use add_module_to_record to place it on a page, then set_module_config_vars to fill its variables." : "Module created but compilation failed: " + (response.data.compile_output || "unknown error"), }, null, 2), }], }; } else { return { content: [{ type: "text", text: JSON.stringify(response.data) }], isError: true }; } } catch (error) { return handleToolError(error, 'create_module', { moduleId }); } }) ); }