ajustes coder
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
import { handleToolError, validateRequired } from "../helpers/errorHandler.js";
|
||||
import { getCurrentProjectInfo, callLocalFileEndpoint, buildLocalFileErrorResponse } from "./helpers.js";
|
||||
import { isProtectedLayoutPath, buildProtectedLayoutPathError } from "./protectedPaths.js";
|
||||
|
||||
export function registerAcaiDeleteTool(server) {
|
||||
server.tool(
|
||||
@@ -16,6 +17,10 @@ export function registerAcaiDeleteTool(server) {
|
||||
const validationError = validateRequired({ file_path }, ["file_path"], "acai-delete");
|
||||
if (validationError) return validationError;
|
||||
|
||||
if (isProtectedLayoutPath(file_path)) {
|
||||
return buildProtectedLayoutPathError(file_path);
|
||||
}
|
||||
|
||||
const { projectSlug, projectDir } = getCurrentProjectInfo();
|
||||
const result = await callLocalFileEndpoint("POST", "/api/files/delete", {
|
||||
project: projectSlug,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
import { handleToolError, validateRequired } from "../helpers/errorHandler.js";
|
||||
import { getCurrentProjectInfo, callLocalFileEndpoint, buildLocalFileErrorResponse } from "./helpers.js";
|
||||
import { isProtectedLayoutPath, buildProtectedLayoutPathError } from "./protectedPaths.js";
|
||||
|
||||
export function registerAcaiLineReplaceTool(server) {
|
||||
server.tool(
|
||||
@@ -24,6 +25,10 @@ export function registerAcaiLineReplaceTool(server) {
|
||||
);
|
||||
if (validationError) return validationError;
|
||||
|
||||
if (isProtectedLayoutPath(file_path)) {
|
||||
return buildProtectedLayoutPathError(file_path);
|
||||
}
|
||||
|
||||
const { projectSlug, projectDir } = getCurrentProjectInfo();
|
||||
const result = await callLocalFileEndpoint("POST", "/api/files/line-replace", {
|
||||
project: projectSlug,
|
||||
|
||||
39
mcp-server/tools/files/protectedPaths.js
Normal file
39
mcp-server/tools/files/protectedPaths.js
Normal file
@@ -0,0 +1,39 @@
|
||||
// Shared guard for generated layout artifacts. The global layout.json and the
|
||||
// custom-header/custom-footer module folders are regenerated from the layout
|
||||
// pipeline (see set_layout_field). Editing them directly leaves the JSON source
|
||||
// out of sync and the visual builder overwrites the agent changes on next save.
|
||||
|
||||
const PROTECTED_LAYOUT_PATHS = [
|
||||
"cms/lib/plugins/builder_saas/layout.json",
|
||||
"template/estandar/modulos/custom-header-twig/",
|
||||
"template/estandar/modulos/custom-footer-twig/",
|
||||
"template/estandar/modulos/custom-header/",
|
||||
"template/estandar/modulos/custom-footer/",
|
||||
];
|
||||
|
||||
// Returns true when `relPath` points at the layout.json or any of the
|
||||
// generated custom-{header,footer}[-twig] module folders.
|
||||
export function isProtectedLayoutPath(relPath) {
|
||||
if (!relPath) return false;
|
||||
const norm = String(relPath).replace(/^\/+/, "");
|
||||
return PROTECTED_LAYOUT_PATHS.some(p => {
|
||||
// Folder entries end with "/" -> prefix match on the normalized path.
|
||||
// File entries (no trailing slash) -> exact match only.
|
||||
if (p.endsWith("/")) return norm === p.slice(0, -1) || norm.startsWith(p);
|
||||
return norm === p;
|
||||
});
|
||||
}
|
||||
|
||||
// Builds a consistent MCP error response pointing the agent to set_layout_field.
|
||||
export function buildProtectedLayoutPathError(relPath) {
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: JSON.stringify({
|
||||
success: false,
|
||||
error: `Forbidden path: ${relPath} is a generated artifact of the global layout. Use set_layout_field instead with field='header' (for custom-header-twig) or field='footer' (for custom-footer-twig).`,
|
||||
}, null, 2),
|
||||
}],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
import { handleToolError, validateRequired } from "../helpers/errorHandler.js";
|
||||
import { getCurrentProjectInfo, callLocalFileEndpoint, buildLocalFileErrorResponse } from "./helpers.js";
|
||||
import { isProtectedLayoutPath, buildProtectedLayoutPathError } from "./protectedPaths.js";
|
||||
|
||||
export function registerAcaiWriteTool(server) {
|
||||
server.tool(
|
||||
@@ -23,6 +24,10 @@ Before writing, check the matching documentation for the file type:
|
||||
const validationError = validateRequired({ file_path }, ["file_path"], "acai-write");
|
||||
if (validationError) return validationError;
|
||||
|
||||
if (isProtectedLayoutPath(file_path)) {
|
||||
return buildProtectedLayoutPathError(file_path);
|
||||
}
|
||||
|
||||
const { projectSlug, projectDir } = getCurrentProjectInfo();
|
||||
const result = await callLocalFileEndpoint("POST", "/api/files/write", {
|
||||
project: projectSlug,
|
||||
|
||||
@@ -13,7 +13,7 @@ import { getCurrentProjectInfo } from "../files/helpers.js";
|
||||
export function registerGetLayoutFieldTool(server) {
|
||||
server.tool(
|
||||
"get_layout_field",
|
||||
`Get the content of a global layout field of the project. Supported: 'style' (global CSS injected in all pages), 'javascript' (global JS), 'header' (HTML/Twig source of the site header), 'footer' (HTML/Twig source of the site footer). Use this before set_layout_field to see the current content.`,
|
||||
`Get the content of a global layout field: 'style', 'javascript', 'header' or 'footer'. For header/footer this is the source of truth — the .tpl files in template/estandar/modulos/custom-{header,footer}-twig/ are generated artifacts. Always prefer this over acai-view on those .tpl files when you need to read the global header/footer source.`,
|
||||
withAuthParams({
|
||||
field: z.enum(["style", "javascript", "header", "footer"]).describe("Which layout field: 'style', 'javascript', 'header' or 'footer'"),
|
||||
}),
|
||||
|
||||
@@ -14,7 +14,7 @@ import { getCurrentProjectInfo } from "../files/helpers.js";
|
||||
export function registerSetLayoutFieldTool(server) {
|
||||
server.tool(
|
||||
"set_layout_field",
|
||||
`Replace the content of a global layout field. 'style'/'javascript' are simple string fields injected via CDN-like URLs (no regeneration needed). 'header'/'footer' are more complex: saving them triggers a server-side pipeline that regenerates the compiled PHP, Twig module files, and TWIG-compiled templates — changes are visible immediately. Destructive: overwrites existing content. Prefer reading with get_layout_field first.`,
|
||||
`Replace the content of a global layout field: 'style' (CSS), 'javascript' (JS), 'header' (Twig source of the site header), 'footer' (Twig source). CRITICAL: for header/footer, ALWAYS use this tool instead of editing template/estandar/modulos/custom-header-twig/index-base.tpl or custom-footer-twig/index-base.tpl directly with acai-line-replace or acai-write. Editing those .tpl files directly leaves layout.json.{header,footer} out of sync and the visual builder will overwrite your changes on its next save. This tool writes the source, syncs layout.json, regenerates the compiled module files, and runs the TWIG compilation in one atomic pipeline. Destructive: overwrites the full content. Pair with get_layout_field first to read the current source.`,
|
||||
withAuthParams({
|
||||
field: z.enum(["style", "javascript", "header", "footer"]).describe("Which layout field: 'style', 'javascript', 'header' or 'footer'"),
|
||||
content: z.string().describe("Full replacement content. Max 500KB."),
|
||||
|
||||
Reference in New Issue
Block a user